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

8xiog9wr  于 2023-10-20  发布在  其他
关注(0)|答案(3)|浏览(140)

我在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==,而自定义实现却不能。

iyzzxitl

iyzzxitl1#

这是设计好的。

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

4如果类定义没有显式声明==运算符函数,而是声明了一个默认的三向比较运算符函数,则==运算符函数被隐式声明,具有与三向比较运算符函数相同的访问权限。类X的隐式声明的==运算符是内联成员,并且在X的定义中被定义为默认值。
只有默认的<=>才允许存在合成的==。基本原理是像std::vector这样的类不应该使用非默认的<=>进行相等性测试。对==使用<=>并不是比较向量的最有效方法。<=>必须给予准确的顺序,而==可能会通过先比较大小而提前退出。
如果一个类在它的三向比较中做了一些特殊的事情,它可能需要在它的==中做一些特殊的事情。因此,该语言将其留给程序员,而不是生成一个潜在的不合理的默认值。

rryofs0p

rryofs0p2#

在这个特性的标准化过程中,决定在逻辑上将相等和排序分开。因此,使用相等测试(==!=)将 * 永远不会 * 调用operator<=>。然而,仍然认为能够用一个声明来默认这两个选项是有用的。因此,如果您默认operator<=>,则决定您也要默认operator==(除非您稍后定义或之前定义)。
至于为什么会做出这样的决定,基本的理由是这样的。以std::string为例。两个字符串的排序是字典式的;每个字符都具有与另一个字符串中的每个字符相比较的整数值。第一个不等式导致排序的结果。
然而,字符串的相等性测试有一个短路。如果两个字符串的长度不相等,那么进行字符比较就没有意义了;他们不平等。所以如果有人在做平等测试,你不想做长形式的测试,如果你可以短路的话。
事实证明,许多需要用户定义排序的类型也会提供一些短路机制来进行相等性测试。为了防止人们只实现operator<=>而放弃潜在的性能,我们有效地强制每个人都这样做。

w1jd8yoj

w1jd8yoj3#

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

相关问题