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 ::
.
This bug report seem to be related Bug 70099 . The namespace of the operator is not considered in the lookup.
The operator is a dependent name [temp.dep]/1.3
:
If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation (17.6.4.1) in both the context of the template definition and the context of the point of instantiation
and by [temp.dep.res]
In resolving dependent names, names from the following sources are considered: 1. Declarations that are visible at the point of definition of the template. 2. Declarations from namespaces associated with the types of the function arguments both from the instantiation context (17.6.4.1) and from the definition context.
and the declaration of the operator is in neither of the contexts nor in the associated namespaces of N::A
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With