【问题标题】:non-defaulted operator <=> doesn't generate == and != in C++20非默认运算符 <=> 在 C++20 中不会生成 == 和 !=
【发布时间】:2020-03-05 22:17:16
【问题描述】:

我在 C++20 中使用新的宇宙飞船运算符 &lt;=&gt; 遇到了一个奇怪的行为。我正在使用带有 /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;
    }
};

我收到以下编译器错误:

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

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

我希望能解释一下为什么默认实现会正确生成operator==,而自定义实现却没有。

【问题讨论】:

  • 标题使得在谷歌搜索时更难找到这个问题。也许应该改为non-defaulted operator &lt;=&gt; doesn't generate == and !=。刚好遇到p1185r2后面的motivation,本来想问类似的问题自己回答。

标签: c++ c++20 spaceship-operator


【解决方案1】:

这是设计使然。

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

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

只有默认的&lt;=&gt; 允许合成的== 存在。理由是像std::vector 这样的类不应该使用非默认的&lt;=&gt; 进行相等测试。将&lt;=&gt; 用于== 并不是比较向量的最有效方法。 &lt;=&gt; 必须给出确切的顺序,而== 可能会通过先比较大小来提前放弃。

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

【讨论】:

  • 这当然是明智的,除非宇宙飞船有越野车。虽然可能效率极低......
  • @Deduplicator - 敏感性是主观的。有人会说默默生成的低效实现是不明智的。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 也许注意默认的operator==没有使用operator&lt;=&gt;
    • @Michaël 好点,谢谢
    猜你喜欢
    • 2021-04-04
    • 2021-05-11
    • 1970-01-01
    • 2021-11-02
    • 2013-08-09
    • 2014-10-28
    • 1970-01-01
    相关资源
    最近更新 更多