如果运算符已经足够,为什么我必须提供运算符?

#include <compare>

struct A
{
    int n;

    auto operator<=>(A const& other) const
    {
        if (n < other.n)
        {
            return std::strong_ordering::less;
        }
        else if (n > other.n)
        {
            return std::strong_ordering::greater;
        }
        else
        {
            return std::strong_ordering::equal;
        }
    }

    // compile error if the following code is commented out.
    // bool operator==(A const& other) const
    // { return n == other.n; }
};

int main()
{   
    A{} == A{};
}

参见online demo

为什么我必须提供operator ==如果operator <=>就足够了?


解决方案

如果operator<=>就足够了,为什么必须提供operator==

嗯,主要是因为不够:-)

当涉及到C++重写语句时,相等和排序是不同的桶:

平等 排序
小学 == <;=>;
二级 != <;,>;,<;=,>;=

主运算符可以反转,次运算符可以根据其对应的主运算符重写:

  • 反转表示a == b可以是:
    • a.operator==(b)(如果有);或
    • b.operator==(a)如果不是,
  • 重写意味着a != b可以:
    • ! a.operator==(b)如果可用
如果您必须重写和反转它,最后一个也可能是! b.operator==(a)(我不完全确定,因为我的经验主要是比较相同类型)。

但默认情况下不跨相等/排序边界进行重写的要求意味着<=>不是==的重写候选者。


相等性和顺序如此分离的原因this P1185 paper,可在讨论此问题的众多标准会议之一中找到。

在许多情况下,根据<=>自动实现==可能会非常低效。想到的是字符串、向量、数组或任何其他集合。您可能不想使用<=>来检查两个字符串是否相等:

  • "xxxxx(a billion other x's)";和
  • "xxxxx(a billion other x's)_and_a_bit_more"

这是因为<=>必须处理整个字符串以确定排序,然后检查排序是否强相等。

但简单的长度检查会很快告诉您它们是不相等的。这就是O(N)时间复杂度和O(1)之间的差异。O(N)时间复杂度是10亿次左右的比较,而O(1)是近乎即时的结果。


您总是可以默认相等,如果您知道这是可以接受的(或者您乐于接受它可能带来的任何性能冲击)。但最好不要让编译器为您做出决定。

更详细地说,请考虑以下完整程序:

#include <iostream>
#include <compare>

class xyzzy {
public:
    xyzzy(int data) : n(data) { }

    auto operator<=>(xyzzy const &other) const {
        // Could probably just use: 'return n <=> other.n;'
        // but this is from the OPs actual code, so I didn't
        // want to change it too much (formatting only).

        if (n < other.n) return std::strong_ordering::less;
        if (n > other.n) return std::strong_ordering::greater;
        return std::strong_ordering::equal;
    }

    //auto operator==(xyzzy const &other) const {
    //    return n == other.n;
    //}

    //bool operator==(xyzzy const &) const = default;

private:
    int n;
};

int main() {
    xyzzy twisty(3);
    xyzzy passages(3);

    if (twisty < passages) std::cout << "less
";
    if (twisty == passages) std::cout << "equal
";
}
它不会按原样编译,因为它的最终语句需要operator==。但是您不必提供一个真正的块(第一个注释掉的块),您只需告诉它使用缺省的块(第二个)。在这种情况下,这可能是正确的决定,因为使用默认设置不会对性能产生实际影响。


请记住,只有在显式提供三向比较运算符(当然使用==!=)的情况下才需要提供相等运算符。如果两者都不提供,C++将为您提供两个缺省值。

而且,即使您必须提供两个函数(其中一个可能是默认的),它仍然比以前更好,在以前,您必须显式地提供它们全部,类似于:

  • a == b
  • a < b
  • a != b,定义为! (a == b)
  • a > b,定义为! (a < b || a == b)
  • a <= b,定义为a < b || a == b
  • a >= b,定义为! (a < b)

相关文章