I have a case where lookup and overload resolution behaves differently:
I cannot figure what exact parts of the standard justify these differences.
Consider the following C++11 code:
#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;
}
Output is (same with g++ 6.3 and 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
Questions:
use1<string>
is different from use1<Test2>
? Both types are declared "at the top", both f() overloads are declared at the bottom.use1<Test2>
is different from use1<double>
? Corresponding f() overloads are on adjacent lines, is there anything special in treatment of built-in types?use1<Test2>
is different from use2<Test2>
? The type of a pointer to function in use2 seems to match the calling context in use1.Two-phase name lookup. At the point where use1
is defined, three overloads of f
are visible via normal lookup. At the point of instantiation, additional overloads may be found - but only by argument-dependent lookup. Test2
is in global namespace, so f(Test2)
is found by ADL; whereas string
is in namespace std
, and so f(string)
(which is, obviously, not in namespace std
) is not found by ADL. double
has no associated namespaces, and so ADL doesn't kick in at all.
In use2
, f
is not a dependent name, and so the second-phase lookup is not performed at all - only overloads visible at the point of definition are considered.