重载解析、名称查找和函数指针
Overload resolution, name lookup and function pointers
我有一个案例,其中查找和重载解析的行为不同:
- 对于用户定义的 class vs 内置类型 vs std::string
- 直接调用vs函数指针调用
我无法确定标准的哪些确切部分证明了这些差异。
考虑以下 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
问题:
- 为什么
use1<string>
与 use1<Test2>
不同?两种类型都声明为 "at the top",两个 f() 重载都在底部声明。
- 为什么
use1<Test2>
与 use1<double>
不同?相应的 f() 重载在相邻的行上,对内置类型的处理有什么特别之处吗?
- 为什么
use1<Test2>
与 use2<Test2>
不同? use2 中指向函数的指针类型似乎与 use1 中的调用上下文匹配。
两阶段名称查找。在定义 use1
的位置,通过正常查找可以看到 f
的三个重载。在实例化时,可能会发现额外的重载 - 但只能通过依赖于参数的查找来找到。 Test2
在全局命名空间中,所以 f(Test2)
被 ADL 找到;而 string
在命名空间 std
中,因此 ADL 找不到 f(string)
(显然不在命名空间 std
中)。 double
没有关联的名称空间,因此 ADL 根本不会启动。
在 use2
中,f
不是从属名称,因此根本不执行第二阶段查找 - 仅考虑在定义点可见的重载。
我有一个案例,其中查找和重载解析的行为不同:
- 对于用户定义的 class vs 内置类型 vs std::string
- 直接调用vs函数指针调用
我无法确定标准的哪些确切部分证明了这些差异。
考虑以下 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
问题:
- 为什么
use1<string>
与use1<Test2>
不同?两种类型都声明为 "at the top",两个 f() 重载都在底部声明。 - 为什么
use1<Test2>
与use1<double>
不同?相应的 f() 重载在相邻的行上,对内置类型的处理有什么特别之处吗? - 为什么
use1<Test2>
与use2<Test2>
不同? use2 中指向函数的指针类型似乎与 use1 中的调用上下文匹配。
两阶段名称查找。在定义 use1
的位置,通过正常查找可以看到 f
的三个重载。在实例化时,可能会发现额外的重载 - 但只能通过依赖于参数的查找来找到。 Test2
在全局命名空间中,所以 f(Test2)
被 ADL 找到;而 string
在命名空间 std
中,因此 ADL 找不到 f(string)
(显然不在命名空间 std
中)。 double
没有关联的名称空间,因此 ADL 根本不会启动。
在 use2
中,f
不是从属名称,因此根本不执行第二阶段查找 - 仅考虑在定义点可见的重载。