给出以下代码:
#include <iostream>
struct Alice
{
template <typename A>
void operator|(const A& /*a*/) const
{
std::cout << "operator| member" << std::endl;
}
};
template <typename A>
void operator|(const A& /*a*/, const Alice& /*alice*/)
{
std::cout << "operator| non-member" << std::endl;
}
int main()
{
Alice a;
Alice b;
a | b;
return 0;
}
它在没有警告的情况下编译GCC 4.8.1,4.9和clang 3.4,但会得到不同的结果。
$ g++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| non-member
$ clang++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| member
造成这种差异的原因是什么?我怎么能强迫同样的行为?
编辑:有趣的事实:从成员函数中删除const
限定符会使gcc更喜欢成员函数。但是,它并没有解决问题。
编辑:如果未指定-std=c++11
,则clang ++更喜欢非会员。
编辑: ICC 14.0更喜欢非会员,不会发出任何警告。
答案 0 :(得分:8)
根据重载决策,有两个可行的函数:全局operator|
的特化 - 带有推导参数的模板和成员操作符函数模板的特化。两者都具有相同的签名 - 成员函数模板具有类型为Alice const&
的隐式对象参数(参见§13.3.1/ 4)。
因此两个可行的函数(在模板参数推导之后)具有相同的签名。并且它们实例化的模板都不比另一个模板更专业。所以这确实是一种含糊不清,因此形成不良。令人惊讶的是,VC ++是正确的。
我如何强制执行相同的行为?
也许你应该删除歧义,Clang,VC ++和GCC应该有相同的行为。