表达式中的运算符的GCC和ADL

AnT*_*AnT 10 c++ gcc operator-overloading language-lawyer argument-dependent-lookup

考虑这个代码示例

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);    
}
Run Code Online (Sandbox Code Playgroud)

代码无法在GCC和Clang中编译,因为常规查找和ADL都无法解析bar来自的调用foo.这是完全可以预期的,因为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);    
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

它将再次无法在GCC编译.因此,似乎特别针对运算符表达式表示法给出了某种特殊处理,而不是函数调用表示法.

这个行为标准呢?我似乎没有在文档的文本中找到它(搜索"关联命名空间"),虽然我依稀记得读到有关GCC这种特性的一些内容.

Bar*_*rry 8

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

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

非成员候选集是operator@根据通常的非限定函数调用([basic.lookup.argdep]中的名称查找规则)在表达式的上下文中进行非限定查找的结果,除了忽略所有成员函数.

非限定函数调用中通常的名称查找规则不包括全局命名空间:[basic.lookup.argdep]/2:

如果T是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类.其关联的命名空间是其关联类的最内部封闭命名空间.

N::A是一个类类型,它的关联类本身,其关联的命名空间是最内部的封闭命名空间,这是N,而不是::.