The code is valid, so msvc and icc are incorrect.
Since an argument of bar
is type-dependent, the name bar
is a dependent name, and is looked up only when the template OtherFunction
is instantiated, not when the template is defined.
C++17 [temp.dep.candidate]/1:
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
For the part of the lookup using unqualified name lookup ([basic.lookup.unqual]), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations from either the template definition context or the template instantiation context are found.
So jumping to [basic.lookup.argdep]/3:
Let X be the lookup set produced by unqualified lookup ([basic.lookup.unqual]) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains
- a declaration of a class member, or
- a block-scope function declaration that is not a using-declaration, or
- a declaration that is neither a function nor a function template
then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of X and Y.
[The current C++20 draft has rearranged wordings in these sections. In particular, the rule about including the instantiation context for lookup of a dependent name in associated namespaces is now listed in [basic.lookup.argdep]/4.5, and is just a Note in [temp.dep.candidate]. I'm not sure if the reason for this is just for clarity, or might have something to do with effects of modules.]
X is the result of unqualified lookup for the name bar
considering only declarations visible from the template definition context. But since the template definition context is the very beginning of your translation unit, obviously X is empty.
Since X doesn't contain anything at all, it doesn't contain the listed items which would force Y to be empty. So to determine Y, we look in the namespaces associated with the argument types. The argument type in this instantiation is hidden::Foo
, so the only associated namespace is hidden
, and the single result of name lookup is function hidden::bar
.
::bar
is not visible in this name lookup, so the bar(T{})
expression cannot be ambiguous.