在 C + + 20中,非默认运算符 < = > 不生成 = = 和! =

我遇到了一个奇怪的行为与新的宇宙飞船操作员 <=>在 C + + 20。我使用的是带有 /std:c++latest的 VisualStudio2019编译器。

正如所料,这段代码编译得很好:

#include <compare>


struct X
{
int Dummy = 0;
auto operator<=>(const X&) const = default; // Default implementation
};


int main()
{
X a, b;


a == b; // OK!


return 0;
}

但是,如果我将 X改为:

struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
};

我得到以下编译器错误:

error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator

我也在叮当声中尝试了这种方法,我得到了类似的行为。

我希望能够解释一下为什么默认实现正确地生成了 operator==,但是自定义实现却不能。

4671 次浏览

在这个特性的标准化过程中,决定了等式和顺序应该在逻辑上分开。因此,使用相等测试(==!=)将使 永远不会调用 operator<=>。但是,能够使用一个声明默认两者仍然被认为是有用的。因此,如果您默认 operator<=>,那么就决定您也要默认 operator==(除非您以后定义它或者以前定义过它)。

至于 为什么要做这个决定,基本的推理是这样的。考虑 std::string。对两个字符串进行排序是按字典顺序进行的; 每个字符都有其整数值与另一个字符串中的每个字符进行比较。第一个不等式导致排序的结果。

然而,字符串的相等性测试有一个短路。如果两个字符串的长度不相等,那么根本没有必要进行字符方面的比较; 它们不相等。因此,如果有人正在做相等测试,你不想做它长形式,如果你可以短路。

事实证明,许多需要用户定义排序的类型也将提供一些用于相等性测试的短路机制。为了防止人们只实现 operator<=>并抛弃潜在的性能,我们有效地强迫每个人都这样做。

这是设计好的。

[ class.compare.default ] (强调 mine)

3 如果类定义没有显式声明一个 == 运算符函数,但声明一个 < strong > 默认的三向比较 运算符 函数,则隐式声明一个 ==运算符函数 具有与三路比较运算符函数相同的访问权限。 类 X 的隐式声明的 ==运算符是内联的 成员,并且在 X 的定义中定义为默认值。

只有默认的 <=>允许合成的 ==存在。其基本原理是像 std::vector这样的类不应该使用非默认的 <=>进行相等性测试。对于 ==使用 <=>不是比较向量的最有效方法。<=>必须给出准确的顺序,而 ==可能会提前通过比较大小第一。

如果一个类在它的三方比较中做了一些特殊的事情,那么它很可能需要在它的 ==中做一些特殊的事情。因此,这种语言不会生成一个潜在的不合理的默认值,而是让程序员自己决定。

其他的答案很好地解释了为什么语言是这样的。我只是想补充一点,如果它不明显,当然可以有一个用户提供的具有默认 operator==operator<=>。您只需要显式地编写默认的 operator==:

struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
bool operator==(const X& other) const = default;
};

注意,默认的 operator==执行成员方向的 ==比较。也就是说,它不是根据用户提供的 operator<=>来实现的。因此,要求程序员明确要求这是一个小的安全特性,以帮助防止意外。