foo(tag<int>{});
triggers the implicit instantiation of a specialization of the function call operator member function template of foo
's closure type with the template argument tag<int>
. This creates a point of instantiation for this member function template specialization. According to [temp.point]/1:
For a function template specialization, a member function template
specialization, or a specialization for a member function or static
data member of a class template, if the specialization is implicitly
instantiated because it is referenced from within another template
specialization and the context from which it is referenced depends on
a template parameter, the point of instantiation of the specialization
is the point of instantiation of the enclosing specialization.
Otherwise, the point of instantiation for such a specialization
immediately follows the namespace scope declaration or definition
that refers to the specialization.
(emphasis mine)
So, the point of instantiation is immediately after main
's definition, before the namespace-scope definition of bar
.
Name lookup for bar
used in decltype(bar(x))
proceeds according to [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
(6.4.1, 6.4.2) except that:
(1.1) — For the part of the lookup using unqualified name lookup
(6.4.1), only function declarations from the template definition
context are found.
(1.2) — For the part of the lookup using associated namespaces
(6.4.2), only function declarations found in either the template
definition context or the template instantiation context are found. [...]
Plain unqualified lookup in the definition context doesn't find anything. ADL in the definition context doesn't find anything either. ADL in the instantiation context, according to [temp.point]/7:
The instantiation context of an expression that depends on the
template arguments is the set of declarations with external linkage
declared prior to the point of instantiation of the template
specialization in the same translation unit.
Again, nothing, because bar
hasn't been declared at namespace scope yet.
So, the compilers are correct. Moreover, note [temp.point]/8:
A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered
a point of instantiation. A specialization for a class template has
at most one point of instantiation within a translation unit. A
specialization for any template may have points of instantiation in
multiple translation units.
If two different points of instantiation give a template specialization different meanings according to the one-definition rule
(6.2), the program is ill-formed, no diagnostic required.
(emphasis mine)
and the second part of [temp.dep.candidate]/1:
[...] If the call would be ill-formed or would find a better match had
the lookup within the associated namespaces considered all the
function declarations with external linkage introduced in those
namespaces in all translation units, not just considering those
declarations found in the template definition and template
instantiation contexts, then the program has undefined behavior.
So, ill-formed NDR or undefined behavior, take your pick.
Let's consider the example from your comment above:
template <class T>
struct tag { };
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
tag<int> bar(tag<int>) { return {}; }
int main() {
auto foo = build();
foo(tag<int>{});
}
Lookup in the definition context still doesn't find anything, but the instantiation context is immediately after main
's definition, so ADL in that context finds bar
in the global namespace (associated with tag<int>
) and the code compiles.
Let's also consider AndyG's example from his comment above:
template <class T>
struct tag { };
//namespace{
//tag<int> bar(tag<int>) { return {}; }
//}
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
namespace{
tag<int> bar(tag<int>) { return {}; }
}
int main() {
auto foo = build();
foo(tag<int>{});
}
Again, the instantiation point is immediately after main
's definition, so why isn't bar
visible? An unnamed namespace definition introduces a using-directive for that namespace in its enclosing namespace (the global namespace in this case). This would make bar
visible to plain unqualified lookup, but not to ADL according to [basic.lookup.argdep]/4:
When considering an associated namespace, the lookup is the same as
the lookup performed when the associated namespace is used as a
qualifier (6.4.3.2) except that:
(4.1) — Any using-directives in the associated namespace are
ignored. [...]
Since only the ADL part of the lookup is performed in the instantiation context, bar
in the unnamed namespace is not visible.
Commenting out the lower definition and uncommenting the upper one makes bar
in the unnamed namespace visible to plain unqualified lookup in the definition context, so the code compiles.
Let's also consider the example from your other comment above:
template <class T>
struct tag { };
int main() {
void bar(int);
auto foo = [](auto x) -> decltype(bar(decltype(x){})) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{});
foo(tag<int>{});
}
tag<int> bar(tag<int>) { return {}; }
This is accepted by GCC, but rejected by Clang. While I was initially quite sure that this is a bug in GCC, the answer may actually not be so clear-cut.
The block-scope declaration void bar(int);
disables ADL according to [basic.lookup.argdep]/3:
Let X be the lookup set produced by unqualified lookup (6.4.1) and let
Y be the lookup set produced by argument dependent lookup (defined as
follows). If X contains
(3.1) — a declaration of a class member, or
(3.2) — a block-scope function declaration that is not a
using-declaration, or
(3.3) — a declaration that is neither a function nor a function
template
then Y is empty. [...]
(emphasis mine)
Now, the question is whether this disables ADL in both the definition and instantiation contexts, or only in the definition context.
If we consider ADL disabled in both contexts, then:
- The block-scope declaration, visible to plain unqualified lookup in the definition context, is the only one visible for all instantiations of the closure type's member function template specializations. Clang's error message, that there's no viable conversion to
int
, is correct and required - the two quotes above regarding ill-formed NDR and undefined behavior don't apply, since the instantiation context doesn't influence the result of name lookup in this case.
- Even if we move
bar
's namespace-scope definition above main
, the code still doesn't compile, for the same reason as above: plain unqualified lookup stops when it finds the block-scope declaration void bar(int);
and ADL is not performed.
If we consider ADL disabled only in the definition context, then:
- As far as the instantiation context is concerned, we're back to the first example; ADL still can't find the namespace-scope definition of
bar
. The two quotes above (ill-formed NDR and UB) do apply however, and so we can't blame a compiler for not issuing an error message.
- Moving
bar
's namespace-scope definition above main
makes the code well-formed.
- This would also mean that ADL in the instantiation context is always performed for dependent names, unless we have somehow determined that the expression is not a function call (which usually involves the definition context...).
Looking at how [temp.dep.candidate]/1 is worded, it seems to say that plain unqualified lookup is performed only in the definition context as a first step, and then ADL is performed according to the rules in [basic.lookup.argdep] in both contexts as a second step. This would imply that the result of plain unqualified lookup influences this second step as a whole, which makes me lean towards the first option.
Also, an even stronger argument in favor of the first option is that performing ADL in the instantiation context when either [basic.lookup.argdep]/3.1 or 3.3 apply in the definition context doesn't seem to make sense.
Still... it may be worth asking about this one on std-discussion.
All quotes are from N4713, the current standard draft.
main()
to get it working:Sample1, Sample2 – Squirearchytag<int>
is in global namespace isn't it? – Caaba