为什么要使用三向比较运算符 (<=>) 而不是双向比较运算符?这有优势吗?

Why should I use the three-way comparison operator (<=>) instead of the two-way comparison operators? Does this have an advantage?

#include <compare>
#include <iostream>

int main()
{ 
   auto comp1 = 1.1 <=> 2.2;
   auto comp2 = -1 <=> 1;
   std::cout << typeid(comp1).name()<<"\n"<<typeid(comp2).name();
}

输出:

struct std::partial_ordering
struct std::strong_ordering

我知道如果操作数是整数类型,运算符returns一个PRvalue类型std::strong_ordering。我也知道操作数是否具有浮点类型,运算符会产生类型为 std::partial_ordering.

PRvalue

但是为什么我要使用三向比较运算符而不是双向比较运算符(==, !=, <, <=, >, >=)?这对我有什么好处吗?

主要优点(至少对我而言)是这个运算符可以默认为 class,这将自动支持您的 class 所有可能的比较。即

#include <compare>

struct foo {
    int a;
    float b;
    auto operator<=>(const foo& ) const = default;
};

// Now all operations used before are defined for you automatically!

auto f1(const foo& l, const foo& r) {
    return l < r;
}

auto f2(const foo& l, const foo& r) {
    return l > r;
}

auto f3(const foo& l, const foo& r) {
    return l == r;
}

auto f4(const foo& l, const foo& r) {
    return l >= r;
}

auto f5(const foo& l, const foo& r) {
    return l <= r;
}

auto f6(const foo& l, const foo& r) {
    return l != r;
}

以前,所有这些操作都必须在 class 中定义,这很麻烦且容易出错 - 因为每当新成员添加到 [=15] 时,都必须记住重新访问这些操作=].

可以在一次操作中确定顺序
其他运算符需要进行两次比较。

其他算子总结:

  • 如果a == b为假,你不知道是a < b还是a > b
  • 如果a != b为真,你不知道是a < b还是a > b
  • 如果a < b为假,你不知道是a == b还是a > b
  • 如果a > b为假,你不知道是a == b还是a < b
  • 如果a <= b为真,你不知道是a == b还是a < b
  • 如果a >= b为真,你不知道是a == b还是a > b

一个巧妙的副作用是所有其他运算符都可以根据 <=> 实现,并且编译器可以为您生成它们。

另一个副作用是,人们可能会对使用 <=> 作为数学中的等价箭头感到困惑,自打字机使用这三个符号以来,这几乎一直存在。
(当且仅当 ab 等价时,我个人对 a <=> b 是“真实的”感到非常恼火。)

请自行判断。

飞船算子的重点是不是专门比较对象的。它的要点是允许编译器从 spaceship 运算符合成 other 比较运算符。

如果您不需要特别回答小于、等于或大于的问题,则无需直接调用它。使用对您有意义的运算符。

但是如果您需要使一个类型具有可比性,您只需编写 2 个函数(spaceship 和 equality)而不是 6 个。并且在编写这样的函数时,您可以在相关的各个类型上使用 spaceship 运算符(他们应该以这种方式进行比较)。这使得实现此类功能变得更加容易。

宇宙飞船操作员允许您做的另一件有用的事情是告诉您 种类 订购比较将提供什么。部分的,强烈的,或其他什么。这在理论上是有用的,但总体上是相当罕见的。

spaceship operator 由 Herb Sutter 提出并被委员会采纳用 C++ 20 实现,详细报告可以 consulted here, or you if are more into lectures, here you can see a video 本人制作它的情况。在报告的第 3/4 页,您可以看到主要用例:

C++20 之前版本所需的比较运算符实现如下 class:

class Point
{
    int x;
    int y;

public:
    friend bool operator==(const Point &a, const Point &b) { return a.x == b.x && a.y == b.y; }
    friend bool operator<(const Point &a, const Point &b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }
    friend bool operator!=(const Point &a, const Point &b) { return !(a == b); }
    friend bool operator<=(const Point &a, const Point &b) { return !(b < a); }
    friend bool operator>(const Point &a, const Point &b) { return b < a; }
    friend bool operator>=(const Point& a, const Point& b) { return !(a < b); }
    // ... non-comparisonfunctions ...
};

将被替换为:

class Point
{
    int x;
    int y;

public:
    auto operator<=>(const Point &) const = default; 
    // ... non-comparison functions ...
};

所以为了回答你的问题,将 operator<=> 重载为 class 成员允许你使用 class 对象的所有比较运算符而无需实现它们,默认它也默认 operator==,如果没有另外声明,它会自动实现 operator!=,使所有比较操作都可用于单个表达式。此功能是主要用例。

我想指出,如果 C++20 不引入 the default comparison featurespaceship 运算符将不可能实现。

Defaulted three-way comparison

[...]
Let R be the return type, each pair of subobjects a, b is compared as follows:
[...]
... if R is std::strong_ordering, the result is:

a == b ? R::equal : a < b ? R::less : R::greater

Otherwise, if R is std::weak_ordering, the result is:

a == b ? R::equivalent : a < b ? R::less : R::greater

Otherwise (R is std::partial_ordering), the result is:

a == b ? R::equal : a < b ? R::less : b < a ? R::greater : R::unordered

Per the rules for any operator<=> overload, a defaulted <=> overload will also allow the type to be compared with <, <=, >, and >=.

If operator<=> is defaulted and operator== is not declared at all, then operator== is implicitly defaulted.

它还允许仅默认 operator ==,这将实现 operator !=,尽管不如前者通用,但它也是一个有趣的可能性。