#include <iostream>
template<typename T>
struct A{};
struct Y{
template<typename T>
bool operator==(A<T>){
std::cout<<"#1\n";
return true;
}
};
template<typename T>
bool operator==(T,Y){
std::cout<<"#2\n";
return true;
}
int main(){
A<int> a;
Y y;
a==y;
}
对于上面的代码段,GCC打印#2
,而Clang打印#1
。结果在这里。我认为Clang是对的,因为#2
是表达式a==y
的非重写非成员候选。相反,#1
的合成候选也是一个可行的函数。也就是说,重载集将由两个候选者组成,它们看起来像:
#1'
bool operator==(A<int>,Y); [with T = int] // synthesized candidate for #1
#2'
bool operator==(A<int>,Y);[with T = A<int>]
根据temp. func.order#3
如果通过重写的候选函数([over. match.oper])以相反的参数顺序通过重载解析来考虑其中一个函数模板,则转换后的模板中函数参数的顺序是相反的。
P/A对如下:
transformed type for #1: (A<uniqueT1>, Y) as A
original type for #2: (T, Y) as P
transformed type for #2: (UniqueT2, Y) as A
original type for #1: (Y, A<UniqueT1>) as P
对于上述候选,模板函数的部分排序足以确定哪个是最佳可行的。哪个是子弹over.match.best#2.5
对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是这样,
2.5 F1和F2是函数模板特化,根据[temp. func.order]中描述的部分排序规则,F1的函数模板比F2的模板更特化,或者,如果不是这样,
[2.6-2.7]
2.8 F2是重写的候选([over.match.oper]),而F1不是
这意味着没有必要进入第2.8项。根据上述P/A对,#1
至少与#2
一样专业,但#2
至少不像#1
那样专业;因此,#1'
是部分排序的最佳可行候选者。所以,Clang在这里应该是正确的。
但是,请考虑以下变体片段
#include <iostream>
template<class T>
struct A{};
class Y{};
template<class T>
bool operator==(Y,A<T>){
std::cout<<"#1\n";
return true;
}
template<class T>
bool operator ==(T,Y){
std::cout<<"#2\n";
return true;
}
int main(){
A<int> a;
Y y{};
a == y;
}
此时,Clang和GCC都同意#2
是最佳可行候选者。结果在这里。然而,在我看来,这个例子与第一个相似。仅仅,它将成员候选者更改为非成员候选者。同样,重载集将由两个候选者组成,它们看起来像:
#1''
bool operator==(A<int>,Y); [with T = int] // synthesized candidate for #1
#2''
bool operator==(A<int>,Y); [with T = A<int>]
在这个例子中,部分排序也足以确定哪个候选者是最好的。因此,#1"
仍然应该是最好的。为什么Clang和GCC都认为#2在这个例子中是最好的?
简而言之:您在示例中使用了不同的Clang版本,Clang 11有正确的实现,而Clang 10没有。
在我的回答中,我详细介绍了为什么Clang 11和MSVC在这两种情况下都是对的,GCC是错的。
从[over. match.oper]#3开始,候选包括四套:
对于[…]二元运算符@
[…]四组候选函数,指定成员候选、非成员候选、内置候选和重写候选,构造如下:
在您的情况下,重写的候选者由[over. match.oper]#3.4.4确定:
对于相等运算符,重写的候选还包括一个合成候选,对于表达式y==x
的每个非重写候选,两个参数的顺序相反。
在表达式x==y
的情况下,感兴趣的候选者是:
x. Operers==(y)
运算符==(x, y)
y==x
的非重写候选,即y. Operers==(x)
和Operers==(y,x)
。让我们看看你的两个例子,并确定最佳候选人。
对于表达式a==y
,候选者是:
non-rewritten candidates:
#2 via operator==(a, y)
rewritten candidates:
#1 via y.operator==(a)
为了确定更好的候选者,我们需要应用[over.match.best. General]#2,确定F1
是否比F2
更好的匹配的相关规则是:
(2.5)F1
和F2
是函数模板特化,根据[temp. func.order]中描述的部分排序规则,F1
的函数模板比F2
的模板更特化,或者,如果不是这样,
[...]
(2.8)F2
是重写的候选([over. match.oper]),而F1
不是
如果我们将#1
作为F1
,将#2
作为F2
,我们得到#1
是一个更好的匹配,因为(2.5)适用并且它在(2.8)之前被考虑。Clang 11和最新的MSVC正确地选择#1
作为这里的更好候选者(演示)。
对于表达式a==y
,候选者是:
non-rewritten candidates:
#2 via operator==(a, y)
rewritten candidates:
#1 via operator==(y, a)
应用[over.match.best. General]#2并将#1
作为F1
,将#2
作为F2
,与第一个示例类似,我们得到#1
是一个更好的候选者,因为(2.5)适用并且它在(2.8)之前被考虑。与第一个示例相同,Clang 11和最新的MSVC正确选择#1
作为更好的候选者(演示)。
为什么Clang和GCC都认为#2在这个例子中是最好的?
不要。在您的演示链接中,您使用了Clang 10,而在第一个示例中,您使用了Clang 11。在这两种情况下,Clang 10与GCC的结果相同。
在这两种情况下,#1
都是更好的候选者,Clang 11和最新的MSVC都正确地选择了它。GCC在这两种情况下都失败了,选择了#2
。我的猜测是GCC的[over.match.best. General]#2的实现是不正确的,因为它错误地在(2.5)之前考虑了(2.8),并在这两种情况下选择了未重写的候选者。