重载解析、名称查找和函数指针

Overload resolution, name lookup and function pointers

我有一个案例,其中查找和重载解析的行为不同:

我无法确定标准的哪些确切部分证明了这些差异。

考虑以下 C++11 代码:

#include <iostream>
#include <string>

using namespace std;

struct Test1 {};
struct Test2 {};

template<typename T>
int f(T t) { return 0; }

int f(Test1 t) { return 10; }
int f(int y) { return 20; }

template<typename T>
int use1() { return f(T()); }

template<typename T>
int use2() { auto fp = static_cast<int(*)(T)>(&f); return (*fp)(T()); }

int f(Test2 t) { return 30; }
int f(string s) { return 40; }
int f(double y) { return 50; }

int main() {
    cout << "use1<float>:  " << use1<float>()  << endl;
    cout << "use1<Test1>:  " << use1<Test1>()  << endl;
    cout << "use1<int>:    " << use1<int>()    << endl;
    cout << "use1<Test2>:  " << use1<Test2>()  << endl;
    cout << "use1<string>: " << use1<string>() << endl;
    cout << "use1<double>: " << use1<double>() << endl;
    cout << endl;
    cout << "use2<float>:  " << use2<float>()  << endl;
    cout << "use2<Test1>:  " << use2<Test1>()  << endl;
    cout << "use2<int>:    " << use2<int>()    << endl;
    cout << "use2<Test2>:  " << use2<Test2>()  << endl;
    cout << "use2<string>: " << use2<string>() << endl;
    cout << "use2<double>: " << use2<double>() << endl;
    return 0;
}

输出为(与 g++ 6.3 和 clang++5.0.0 trunk 相同):

use1<float>:  0
use1<Test1>:  10
use1<int>:    20
use1<Test2>:  30
use1<string>: 0
use1<double>: 0

use2<float>:  0
use2<Test1>:  10
use2<int>:    20
use2<Test2>:  0
use2<string>: 0
use2<double>: 0

问题:

  1. 为什么 use1<string>use1<Test2> 不同?两种类型都声明为 "at the top",两个 f() 重载都在底部声明。
  2. 为什么 use1<Test2>use1<double> 不同?相应的 f() 重载在相邻的行上,对内置类型的处理有什么特别之处吗?
  3. 为什么 use1<Test2>use2<Test2> 不同? use2 中指向函数的指针类型似乎与 use1 中的调用上下文匹配。

两阶段名称查找。在定义 use1 的位置,通过正常查找可以看到 f 的三个重载。在实例化时,可能会发现额外的重载 - 但只能通过依赖于参数的查找来找到。 Test2 在全局命名空间中,所以 f(Test2) 被 ADL 找到;而 string 在命名空间 std 中,因此 ADL 找不到 f(string)(显然不在命名空间 std 中)。 double 没有关联的名称空间,因此 ADL 根本不会启动。

use2 中,f 不是从属名称,因此根本不执行第二阶段查找 - 仅考虑在定义点可见的重载。