Why does the following code compile:
template<typename T>
void foo(T in) { bar(in); }
struct type{};
void bar(type) {}
int main() { foo(type()); }
When the following does not:
template<typename T>
void foo(T in) { bar(in); }
void bar(int) {}
int main() { foo(42); }
Compiling with GnuC++ 7:
a.cpp: In instantiation of 'void foo(T) [with T = int]':
a.cpp:9:20: required from here
a.cpp:2:21: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
void foo(T in) { bar(in); }
~~~^~~~
a.cpp:8:6: note: 'void bar(int)' declared here, later in the translation unit void bar(int) {}
I would assume that MSVC would compile both (as it does) but that GCC would reject both since GCC/Clang have proper two phase name lookup...
The strange part is not that the
int
example fails to compile, it is that thetype
example does sincebar
is defined afterfoo
. This is due to [temp.dep.candidate] (see third paragraph).Two-pass compilation of templates
When the compiler parses and compiles a template class or function, it looks up identifiers in two pass:
bar()
depends on a template argument, nothing is done. This lookup is done at the point of definition.You get an error during pass #2.
ADL lookup
When a function name is looked up, it is done within the current context and those of the parameters type. For instance, the following code is valid though
f
is defined in namespacen
:More about ADL (cppreference.com):
Two-pass compilation, ADL lookup and unqualified-id lookup
In your case, those three mechanisms collide. See [temp.dep.candidate]:
So, with
foo(type())
unqualified-id lookup kicks in and the lookup is done "in either the template definition context or the template instantiation".With
foo(42)
,42
being a fundamental type, ADL is not considered and only the "definition context" is considered.