foo
in your case is a dependent name, since function choice depends on the type if the argument and the argument type depends on the template parameter. This means that foo
is looked up in accordance with the rules of dependent lookup.
The difference between dependent and non-dependent lookup is that in case of dependent lookup ADL-nominated namespaces are seen as extended: they are extended with extra names visible from the point of template instantiation (tfoo
call in your case). That includes the names, which appeared after the template declaration. The key point here is that only ADL-nominated namespaces are extended in this way.
(By ADL-nominated namespace I refer to namespace associated with function argument type and therefore brought into consideration by the rules of dependent name lookup. See "3.4.2 Argument-dependent name lookup")
In your case the argument has type int
. int
is a fundamental type. Fundamental types do not have associated namespaces (see "3.4.2 Argument-dependent name lookup"), which means that it does not nominate any namespace through ADL. In your example ADL is not involved at all. Dependent name lookup for foo
in this case is no different from non-dependent lookup. It will not be able to see your foo
, since it is declared below the template.
Note the difference with the following example
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
This code will compile since argument type S
is a class type. It has an associated namespace - the global one - and it adds (nominates) that global namespace for dependent name lookup. Such ADL-nominated namespaces are seen by dependent lookup in their updated form (as seen from the point of the call). This is why the lookup can see foo
and completes successfully.
It is a rather widespread misconception when people believe that the second phase of so called "two-phase lookup" should be able to see everything that was additionally declared below template definition all the way to the point of instantiation (point of the call in this case).
No, the second phase does not see everything. It can see the extra stuff only in namespaces associated with function arguments. All other namespaces do not get updated. They are seen as if observed from the point of template definition.
foo
? Like this: coliru.stacked-crooked.com/a/dc92e572d02f601a – Persis