简短问题:运营商是否有针对内部链接的重载解析的特殊模板查找规则,或者底部的代码是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);
}
以上打印"hello"
,一切都很好。
现在让我们将foo
放在一个匿名命名空间中:
namespace {
template<typename T>
void foo (const A<T>&a) { foo(a.b); }
void foo (const B&) { std::cout << "hello"; }
}
代码现在无法编译。 Clang说error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
和GCC template argument deduction/substitution failed
。
这是因为foo(const B&)
在foo<T>
之后定义且没有外部链接,因此n4296中说明了这一点:
[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;
}
GCC(4.7 / 4.8 / 4.9)现在对代码非常满意,并在-Wall -Wextra -pedantic -ansi
时发出零警告,而clang以与'operator<<'
相同的方式抱怨foo
。< / p>
我没有在标准中找到运算符重载查找的任何异常,所以我认为这是GCC中的一个错误(功能?)但是模板解析规则并不容易,所以我想在提交错误之前我可以在这里查看。 / p>
您可以直接看到此代码here。
答案 0 :(得分: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;
}
报告here。