匿名命名空间内的运算符的模板重载解析

Thi*_*aut 12 c++ gcc templates language-lawyer argument-dependent-lookup

简短问题:运营商是否具有针对内部链接的重载解析的特殊模板查找规则,或者底层的代码是GCC中运营商的模板重载解决错误?

细节:我会带你完成我的推理,而不是粘贴一大堆代码.让我们从一些简单的代码开始:

#include <iostream>

template<typename T> struct A{ T b; };
struct B{};

template<typename T> 
void foo (const A<T>&a) { foo(a.b); } 
void foo (const B&) { std::cout << "hello"; }

int main(){
  A<B> b;
  foo(b);
}
Run Code Online (Sandbox Code Playgroud)

以上打印"hello",一切都很好.

现在让我们将它们foo放在匿名命名空间中:

namespace {
template<typename T> 
void foo (const A<T>&a) { foo(a.b); } 
void foo (const B&) { std::cout << "hello"; }
}
Run Code Online (Sandbox Code Playgroud)

代码现在无法编译.Clang说error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup和海湾合作委员会template argument deduction/substitution failed.

这是破坏的,因为在n4296中解释了foo(const B&)之后定义foo<T>并且没有外部链接:

[basic.link] 未命名的命名空间或直接或间接在未命名的命名空间内声明的命名空间具有内部链接.所有其他名称空间都有外部链接.

[temp.point] 依赖于模板参数的表达式的实例化上下文是在同一翻译单元中模板特化的实例化之前声明的具有外部链接的声明集 .

[temp.dep.candidate] 对于postfix-expression是从属名称的函数调用,使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:

  • 对于使用非限定名称查找(3.4.1)的查找部分,仅找到模板定义上下文中的函数声明.

  • 对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.

现在使用运算符是一样的:

struct ostream {} cout; 
template<typename T> struct A{ T t; };
struct B{};

namespace {
template<typename T>
ostream& operator<< (ostream& out, const A<T>&v) 
  { return out << v.t; }
ostream& operator<< (ostream& out, const B&) 
  { return out; }
}

int main(){
  A<B> a;
  cout << a; 
}
Run Code Online (Sandbox Code Playgroud)

GCC(4.7/4.8/4.9)现在对代码非常满意并且给出了零警告,-Wall -Wextra -pedantic -ansi而clang抱怨'operator<<'的方式与此相同foo.

我没有发现标准中的运算符重载查找有任何异常,所以我认为这是GCC中的一个错误(功能?),但模板解析规则并不容易,所以我想在提交错误之前我可以在这里查看.

你可以在这里看到这段代码.

Thi*_*aut 2

这绝对是 gcc 中的一个错误。下面的代码可以正确地right使用 clang 打印,但wrong使用 GCC 打印。

#include <iostream>

template<typename T> struct A{ T t; };
struct B{};
struct C : public B{};

std::ostream& operator<< (std::ostream& out, const B&) 
  { return out << "right"; }

namespace {
template<typename T>
std::ostream& operator<< (std::ostream& out, const A<T>&v) 
  { return out << v.t; }

std::ostream& operator<< (std::ostream& out, const C&) 
  { return out << "wrong"; }
}

int main(){
  A<C> a;
  std::cout << a;
}
Run Code Online (Sandbox Code Playgroud)

报道到这里了