除了函数调用表达式之外,重载函数的名字还可以出现在下列 7 种发生重载决议的语境中:

1) 对象或引用声明中的初始化器

2) 赋值运算符的右侧

3) 作为函数调用的实参

4) 作为用户定义运算符的实参

5) return 语句

6) 显式转型static_cast 的实参

7) 非类型模板实参

在每个语境中,重载函数的名称可以前附取址运算符 & 并且可以被一组冗余的括号所环绕。

在所有这些语境中,从重载集中选择的函数,是其类型与目标所期待的函数指针、函数引用或成员函数指针类型相匹配的函数,目标分别为:被初始化的对象或引用,赋值的左侧,函数或运算符的形参,函数的返回类型,转型的目标类型,以及模板形参的类型。

函数的形参类型和返回类型必须与目标严格匹配,不考虑隐式转换(例如,在初始化指向返回基类指针的函数的指针时,不会选择返回派生类指针的函数)。

若函数名指名某个函数模板,则首先进行模板实参推导,而若其成功,则将生成一个单独的模板特化并添加到所要考虑的重载集合中。从集合中丢弃所有不满足其关联制约的函数。 (C++20 起)若集合中有多于一个函数与目标匹配,且至少一个函数是非模板,则从考虑中去除模板特化。对于任何一对非模板函数,其中之一比另一个更受制约,则从集合中丢弃受较少制约的函数。 (C++20 起)若所有剩余候选者都是模板特化,则当存在更特殊的模板特化时,移除较不特殊者。若在各项移除之后仍留有多于一个候选者,则程序非良构。

示例

运行此代码

  1. int f(int) { return 1; }
  2. int f(double) { return 2; }
  3.  
  4. void g( int(&f1)(int), int(*f2)(double) ) {}
  5.  
  6. template< int(*F)(int) >
  7. struct Templ {};
  8.  
  9. struct Foo {
  10. int mf(int) { return 3; }
  11. int mf(double) { return 4; }
  12. };
  13.  
  14. struct Emp {
  15. void operator<<(int (*)(double)) {}
  16. };
  17.  
  18. int main()
  19. {
  20. // 1. 初始化
  21. int (*pf)(double) = f; // 选择 int f(double)
  22. int (&rf)(int) = f; // 选择 int f(int)
  23. int (Foo::*mpf)(int) = &Foo::mf; // 选择 int mf(int)
  24.  
  25. // 2. 赋值
  26. pf = nullptr;
  27. pf = &f; // 选择 int f(double)
  28.  
  29. // 3. 函数实参
  30. g(f, f); // 为第一实参选择 int f(int)
  31. // 而为第二实参选择 int f(double)
  32.  
  33. // 4. 用户定义运算符
  34. Emp{} << f; // 选择 int f(double)
  35.  
  36. // 5. 返回值
  37. auto foo = []() -> int (*)(int) {
  38. return f; // 选择 int f(int)
  39. };
  40.  
  41. // 6. 转型
  42. auto p = static_cast<int(*)(int)>(f); // 选择 int f(int)
  43.  
  44. // 7. 模板实参
  45. Templ<f> t; // selects int f(int)
  46. }

引用

  • 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]