Namespaces
Variants

Address of an overloaded function

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

除了发生 函数调用表达式 的场合(此处会进行 重载解析 ),重载函数的名称还可能出现在以下7种上下文中:

# 上下文 目标
1 对象或 引用 声明中的 初始化器 被初始化的对象或引用
2 内置赋值表达式的右侧 内置赋值的左侧
3 作为函数调用参数 函数形参
4 作为用户自定义运算符参数 运算符形参
5 return 语句 函数或转换的返回值
6 显式转换 static_cast 参数 对应的转换目标
7 常量 模板参数 对应的模板形参

在每个上下文中,重载函数的名称前可以放置取址运算符 & ,并可用一对冗余的括号将其括起。

若目标类型包含 占位符类型 ,则执行占位符类型推导,以下描述使用推导出的类型作为目标类型。

(since C++26)

目录

选择函数

当获取重载函数的地址时,会从重载函数名称所引用的重载集合中选出一个函数集合 S

  • 若不存在目标,则选择所有已命名的非模板函数。
  • 否则,对于目标类型的函数类型 FT ,若函数类型 F (可能在应用 函数指针转换 后) (C++17 起) FT 相同,则选择该类型为 F 的非模板函数。 [1]
  • 通过 模板实参推导 为每个命名的函数模板生成的特化(若存在)也会被加入 S

若目标为函数指针类型或函数引用类型, S 只能包含非成员函数 、显式对象成员函数 (C++23 起) 以及静态成员函数。若目标为指向成员函数的指针类型, S 只能包含隐式对象成员函数。

  1. 换句话说,当目标类型为指向成员函数的指针类型时,函数所属的类会被忽略。

函数消解

在形成集合 S 后,函数按以下顺序被消除:

  • 所有具有关联 约束 但未被满足的函数都将从 S 中剔除。
(since C++20)
  • 如果 S 中仍存在多个函数,且 S 同时包含非模板函数,则 S 中的所有函数模板特化都将被淘汰。
  • S 包含第二个非模板函数,且该函数在 偏序约束 上比 func 更具约束性,则任何给定的非模板函数 func 将被消除。
(since C++20)
  • 任何给定的函数模板特化 spec 都会被淘汰,如果 S 包含第二个函数模板特化,且其函数模板比 spec 的函数模板 更加特化

经过此类消除(如果有)后, S 中应恰好保留一个被选函数。否则程序将是非良构的。

示例

int f(int) { return 1; }
int f(double) { return 2; }
void g(int(&f1)(int), int(*f2)(double)) { f1(0); f2(0.0); }
template<int(*F)(int)>
struct Templ {};
struct Foo
{
    int mf(int) { return 3; }
    int mf(double) { return 4; }
};
struct Emp
{
    void operator<<(int (*)(double)) {}
};
int main()
{
    // 1. 初始化
    int (*pf)(double) = f; // 选择 int f(double)
    int (&rf)(int) = f; // 选择 int f(int)
    int (Foo::*mpf)(int) = &Foo::mf; // 选择 int mf(int)
    // 2. 赋值
    pf = nullptr;
    pf = &f; // 选择 int f(double)
    // 3. 函数参数
    g(f, f); // 为第一个参数选择 int f(int)
             // 为第二个参数选择 int f(double)
    // 4. 用户定义运算符
    Emp{} << f; // 选择 int f(double)
    // 5. 返回值
    auto foo = []() -> int (*)(int)
    {
        return f; // 选择 int f(int)
    };
    // 6. 类型转换
    auto p = static_cast<int(*)(int)>(f); // 选择 int f(int)
    // 7. 模板参数
    Templ<f> t;  // 选择 int f(int)
    // 防止“未使用变量”警告,类似于 [[maybe_unused]]
    [](...){}(pf, rf, mpf, foo, p, t);
}

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 202 C++98 常量模板参数不是获取重载函数地址的上下文 是此类上下文
CWG 250 C++98 使用非推导模板参数生成的函数模板特化
不会被重载集选中
也会被选中
CWG 1153 C++98 未明确给定函数类型是否匹配目标类型 已明确
CWG 1563 C++11 未明确列表初始化是否是获取重载函数地址的上下文 已明确

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 12.3 重载函数地址 [over.over]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 12.5 重载函数地址 [over.over]
  • C++17 标准 (ISO/IEC 14882:2017):
  • 16.4 重载函数地址 [over.over]
  • C++14 标准 (ISO/IEC 14882:2014):
  • 13.4 重载函数地址 [over.over]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 13.4 重载函数地址 [over.over]
  • C++98 标准 (ISO/IEC 14882:1998):
  • 13.4 重载函数地址 [over.over]