Should this function call be ambiguous?

Gcc and Clang are correct. The global bar defined after the definition of OtherFunction can't be found by name lookup; while hidden::bar could be found by ADL.

(emphasis mine)

For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations with external linkage (until C++11) that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations with external linkage (until C++11) that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).


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.