表达式中运算符的 GCC 和 ADL

GCC and ADL for operators in expressions

考虑此代码示例

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);    
}

代码无法在 GCC 和 Clang 中编译,因为常规查找和 ADL 都无法解析从 foobar 的调用。这是完全可以预料的,因为 bar 调用的关联名称空间列表只是 N。不包括全局名称空间,找不到全局 bar。一切都应该如此。

但是,如果我将其更改为

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);    
}

突然开始在GCC中编译成功。 (同时,Clang 拒绝这两个版本的代码)。

在代码的第二个(基于运算符的)版本中,GCC 似乎也将全局命名空间视为 ADL 的关联命名空间。

如果在后一版本的代码中我将调用更改为

template <class T> void foo(const S<T> &v)
{
  operator +(v.t);
}

它会再次在GCC 中编译失败。因此,似乎对 operators-in-expressions 符号进行了某种特殊处理,而不是 function-call 符号。

这是行为规范吗?我似乎没有在文档的文本中找到它(搜索 "associated namespace"),尽管我依稀记得读过一些关于 GCC 的这种特性的文章。

这是gcc bug 51577。第二个测试用例几乎就是您的代码示例。

对于在全局命名空间中查找的运算符查找没有特殊规则。 [over.match.oper]/3 有:

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.

非限定函数调用中名称查找的通常规则不包括全局命名空间:[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 是一个 class 类型,它关联的 class 是它自己,它关联的命名空间是 innermost 封闭的命名空间,这是只是 N,而不是 ::

此错误报告似乎与 Bug 70099 相关。查找中不考虑运算符的命名空间。

运算符是从属名称[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

以及 [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.

并且运算符的声明既不在上下文中也不在 N::A 的关联命名空间中。