重载函数的地址
除了函数调用表达式之外,重载函数的名字还可以出现在下列 7 种发生重载决议的语境中:
2) 赋值运算符的右侧
3) 作为函数调用的实参
4) 作为用户定义运算符的实参
5) return 语句
7) 非类型模板实参
在每个语境中,重载函数的名称可以前附取址运算符 &
并且可以被一组冗余的括号所环绕。
在所有这些语境中,从重载集中选择的函数,是其类型与目标所期待的函数指针、函数引用或成员函数指针类型相匹配的函数,目标分别为:被初始化的对象或引用,赋值的左侧,函数或运算符的形参,函数的返回类型,转型的目标类型,以及模板形参的类型。
函数的形参类型和返回类型必须与目标严格匹配,不考虑隐式转换(例如,在初始化指向返回基类指针的函数的指针时,不会选择返回派生类指针的函数)。
若函数名指名某个函数模板,则首先进行模板实参推导,而若其成功,则将生成一个单独的模板特化并添加到所要考虑的重载集合中。从集合中丢弃所有不满足其关联制约的函数。 (C++20 起)若集合中有多于一个函数与目标匹配,且至少一个函数是非模板,则从考虑中去除模板特化。对于任何一对非模板函数,其中之一比另一个更受制约,则从集合中丢弃受较少制约的函数。 (C++20 起)若所有剩余候选者都是模板特化,则当存在更特殊的模板特化时,移除较不特殊者。若在各项移除之后仍留有多于一个候选者,则程序非良构。
示例
运行此代码
int f(int) { return 1; } int f(double) { return 2; } void g( int(&f1)(int), int(*f2)(double) ) {} 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; // selects int f(int) }
引用
- C++11 standard (ISO/IEC 14882:2011):
- 13.4 Address of overloaded function [over.over]
- C++98 standard (ISO/IEC 14882:1998):
- 13.4 Address of overloaded function [over.over]