GCC and ADL for operators in expressions
Asked Answered
M

2

10

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.

Moppet answered 27/9, 2018 at 17:29 Comment(0)
T
9

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 ::.

Transudate answered 27/9, 2018 at 17:45 Comment(2)
Wow, that bug is nearly 10 years old!Flunkey
This issue is also demonstrated on a page concerned with GCC/Clang Compatibility here. See the second (Dump) example in the "Unqualified lookup in templates" section.Flunkey
A
3

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.

Antisepticize answered 27/9, 2018 at 17:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.