64

<=>在 C++20 中使用新的宇宙飞船运算符遇到了一个奇怪的行为。我将 Visual Studio 2019 编译器与/std:c++latest.

正如预期的那样,这段代码编译得很好:

#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

我也在clang上试过这个,我得到了类似的行为。

我会很感激一些解释为什么默认实现operator==正确生成,但自定义的没有。

4

3 回答 3

60

这是设计使然。

[class.compare.default](强调我的)

3如果类定义没有显式声明== 操作符函数,而是声明了默认的三向比较操作符函数,则==操作符函数被隐式声明,具有与三向比较操作符函数相同的访问权限。类 X的隐式声明==运算符是内联成员,在 X 的定义中被定义为默认值。

只有一个 defaulted<=>允许一个综合==存在。理由是类std::vector不应该使用非默认<=>的相等测试。使用<=>for==不是比较向量的最有效方法。<=>必须给出确切的顺序,而==可以通过先比较尺寸来提早放弃。

如果一个类在它的三向比较中做了一些特别的事情,它可能需要在它的==. 因此,语言不会生成潜在的不合理的默认值,而是将其留给程序员。

于 2019-11-09T15:56:20.607 回答
54

在此功能的标准化过程中,决定在逻辑上将相等和排序分开。因此,相等测试 ( ==and !=)的使用永远不会调用operator<=>. 但是,能够通过一个声明默认它们两者仍然被认为是有用的。因此,如果您 default operator<=>,则决定您也意味着 default operator==(除非您稍后定义或之前定义了它)。

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

但是,字符串的相等性测试有一个短路。如果两个字符串的长度不相等,那么进行字符比较就没有意义了。他们不平等。因此,如果有人在做平等测试,如果你能把它短路,你就不想做长篇大论。

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

于 2019-11-09T15:54:35.213 回答
25

其他答案很好地解释了为什么这种语言是这样的。我只是想补充一点,以防不明显,当然可以让用户提供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<=>。因此,要求程序员明确要求这是一个有助于防止意外的次要安全功能。

于 2019-11-10T21:05:56.213 回答