没有为C ++ 20中的自定义太空飞船运算符实现定义平等运算符


51

<=>在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==正确生成但自定义代码无法正确生成的一些解释表示赞赏。

Answers:


50

这是设计使然。

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

3如果类定义未显式声明== 运算符,但声明了默认的三向比较运算符==则隐式声明一个运算符,其访问方式与三向比较运算符相同。==类X 的隐式声明的运算符是内联成员,并且在X的定义中定义为默认值。

只有默认值<=>允许合成==存在。理由是像这样的类std::vector不能使用defaulted <=>。另外,使用<=>for ==并不是比较向量的最有效方法。<=>必须给出确切的顺序,而==可能会通过先比较尺寸来提前保释。

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


4
除非飞船是越野车,否则这当然是明智的。潜在的严重低效率...
Deduplicator

1
@Deduplicator-敏感性是主观的。有人会说,无声地生成低效的实现是不明智的。
StoryTeller-Unslander Monica,

45

在此功能的标准化期间,决定逻辑上应将相等性和顺序分开。因此,对等式测试(==!=)的使用将永远不会调用operator<=>。但是,仍然可以使用单个声明将它们都默认为默认值。因此,如果您违约operator<=>,则决定您也要违约。operator==(除非您稍后定义它或早先定义它)。

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

但是,字符串的相等性测试存在短路。如果两个字符串的长度不相等,那么完全没有必要进行字符比较;他们是不平等的。因此,如果有人正在进行相等性测试,那么如果您可以使它短路,那么您就不希望这样做。

事实证明,许多需要用户定义排序的类型也将提供用于相等性测试的某种短路机制。为了防止人们仅执行operator<=>并丢弃潜在的性能,我们有效地迫使每个人都做到。


5
这是比公认的答案更好的解释
备忘

17

其他答案很好地解释了为什么这种语言如此。我只是想补充一点,以防万一,这当然可以让用户提供operator<=>一个defaulted operator==。您只需要显式编写默认值即可operator==

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
    bool operator==(const X& other) const = default;
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.