首页 > 解决方案 > 表达式中运算符的 GCC 和 ADL

问题描述

考虑这个代码示例

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 都无法解析对barfrom的调用foo。这是完全可以预料的,因为barcall 的关联命名空间列表只是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 中编译。因此,似乎对表达式中的运算符符号进行了某种特殊处理,而不是对函数调用符号进行了特殊处理。

它有这种行为标准吗?我似乎没有在文档的文本中找到它(搜索“关联命名空间”),尽管我隐约记得读过一些关于 GCC 的这种特性的东西。

标签: c++gccoperator-overloadinglanguage-lawyerargument-dependent-lookup

解决方案


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

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

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

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

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

N::A是一个类类型,它的关联类是它自己,它的关联命名空间是最里面的封闭命名空间,它只是N,不是::


推荐阅读