Consider this code sample
template <typename T> struct S { T t; };
template <class T> void foo(const S<T> &v)
{
bar(v.t);
}
namespace N
{
struct A {};
}
void bar(const N::A &a) {}
int main()
{
S<N::A> a;
foo(a);
}
The code fails to compile in GCC and Clang, since neither regular lookup nor ADL can resolve the call to bar
from foo
. This is perfectly expected, since the list of associated namespaces for bar
call is just N
. Global namespace is not included, global bar
is not found. All as it should be.
However, if I change it to
template <typename T> struct S { T t; };
template <class T> void foo(const S<T> &v)
{
+v.t;
}
namespace N
{
struct A {};
}
void operator +(const N::A& a) {}
int main()
{
S<N::A> a;
foo(a);
}
It suddenly begins to compile successfully in GCC. (Meanwhile, Clang rejects both versions of the code).
It appears that in the second (operator-based) version of the code GCC considers global namespace as an associated namespace for ADL as well.
If in the latter version of the code I change the call to
template <class T> void foo(const S<T> &v)
{
operator +(v.t);
}
It will again fail to compile in GCC. So, it appears some sort of special treatment is given to operators-in-expressions notation specifically, but not to function-call notation.
It this behavior standard? I don't seem to find it in the text of the document (searching for "associated namespace"), although I do vaguely remember reading something about this peculiarity of GCC.
This is gcc bug 51577. The second test case there is pretty much exactly your code example.
There is no special rule for operator lookup that would look in the global namespace. [over.match.oper]/3 has:
The set of non-member candidates is the result of the unqualified lookup of
operator@
in the context of the expression according to the usual rules for name lookup in unqualified function calls ([basic.lookup.argdep]) except that all member functions are ignored.
The usual rules for name lookup in unqualified function calls does not include the global namespace: [basic.lookup.argdep]/2:
If
T
is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes.
N::A
is a class type, its associated class is itself, its associated namespaces are the innermost enclosing namespaces, which is just N
, not ::
.