非默认的运算符 <=> 不生成 == 和 !=

80
我在C++20中使用新的太空船操作符<=>遇到了一个奇怪的行为。我正在使用带有/std:c++latest的Visual Studio 2019编译器。
这段代码如预期般编译通过:
#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;
    }
};

我遇到了以下编译器错误:
错误 C2676:二进制'==':'X'未定义此运算符或转换为预定义运算符可接受的类型
我也在clang上尝试了这个,结果出现了类似的情况。
我希望能够解释一下为什么默认实现能够正确生成'operator==',但自定义实现却不能。

1
标题使得在谷歌搜索时更难找到这个问题。也许应该改成“非默认运算符<=>不生成==和!=”。我碰巧遇到了p1185r2动机,本来想问一个类似的问题并自己回答它。 - Tony
1
我希望保持这个问题的开放状态,因为我觉得包含规格引用的答案比非重复问题的答案要好得多。 - undefined
3个回答

74
这是有意设计的。
[class.compare.default](我强调)如果类定义没有显式声明一个==运算符函数,但声明了一个默认的三路比较运算符函数,则会隐式声明一个具有与三路比较运算符函数相同访问权限的==运算符函数。类X的隐式声明的==运算符是一个内联成员,并在X的定义中被默认定义。
只有默认的<=>才允许存在合成的==。其理由是像std::vector这样的类不应该使用非默认的<=>进行相等性测试。使用<=>进行==比较不是比较向量的最有效的方法。<=>必须给出精确的排序,而==可以通过首先比较大小来提前退出。
如果一个类在其三路比较中有特殊的操作,那么它很可能需要在其==操作中做一些特殊的处理。因此,语言不会生成可能不合理的默认操作,而是将其留给程序员自行处理。

5
除非宇宙飞船有漏洞,否则这肯定是明智的选择。但有可能效率极低… - Deduplicator
6
敏感度是主观的。有些人会说默默生成的低效实现不合理。 - StoryTeller - Unslander Monica

63
在对这个特性进行标准化时,决定逻辑上应该将等式和排序分开。因此,使用等式测试(==!=永远不会调用operator<=>。然而,仍然认为能够用单个声明默认两者是有用的。 所以如果你默认operator<=>,就意味着你也默认了operator==(除非你之后定义它或者之前已经定义过它)。
至于为什么做出这个决定,基本推理如下。考虑std::string。两个字符串的排序是按字典顺序排列的;每个字符的整数值与另一个字符串中的每个字符进行比较。第一个不相等的字符决定了排序结果。
然而,字符串的等式测试具有短路效应。如果两个字符串长度不相等,则根本没有必要进行逐字符比较;它们不相等。因此,如果有人正在进行等式测试,如果可以短路运算,就不需要进行长形式操作。事实证明,需要用户定义排序的多种类型也会提供一些用于相等测试的短路机制。为了防止人们仅实现operator<=>并放弃潜在的性能,我们有效地强制每个人都执行这两个操作。

28
其他答案已经很好地解释了语言为什么是这样的。我只想补充一点,如果不明显的话,当然可以使用用户提供的具有默认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<=>实现的。因此,要求程序员显式地请求这个操作符是一个小的安全功能,有助于防止出现意外情况。


5
请注意,默认的operator==没有使用operator<=> - Michaël

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接