C++中的<=>("spaceship",三向比较)运算符是什么?

What is the <=> ("spaceship", three-way comparison) operator in C++?

当我尝试学习 C++ 运算符时,我偶然发现了一个关于 cppreference.com* 的奇怪比较运算符在看起来像这样的 table 中:

“好吧,如果这些是 C++ 中的常用运算符,我最好学习它们”,我想。但是我试图阐明这个谜团的所有尝试都没有成功。即使在这里,在 Stack Overflow 上我的搜索也没有成功。

<=>C++有联系吗?

如果有,这个运算符到底做了什么?

* 同时 cppreference.com 更新了该页面,现在包含有关 <=> 运营商的信息。

2017-11-11, the ISO C++ committee adopted Herb Sutter's proposal for the <=> "spaceship" three-way comparison operator as one of the new features that were added to C++20. In the paper titled Consistent comparison 上,Sutter、Maurer 和 Brown 演示了新设计的概念。有关该提案的概述,以下是文章的摘录:

The expression a <=> b returns an object that compares <0 if a < b, compares >0 if a > b, and compares ==0 if a and b are equal/equivalent.

Common case: To write all comparisons for your type X with type Y, with memberwise semantics, just write:

auto X::operator<=>(const Y&) =default;

Advanced cases: To write all comparisons for your type X with type Y, just write operator<=> that takes a Y, can use =default to get memberwise semantics if desired, and returns the appropriate category type:

  • Return an _ordering if your type naturally supports <, and we’ll efficiently generate symmetric <, >, <=, >=, ==, and !=; otherwise return an _equality, and we’ll efficiently generate symmetric == and !=.
  • Return strong_ if for your type a == b implies f(a) == f(b) (substitutability, where f reads only comparison-salient state that is accessible using the public const members), otherwise return weak_.

比较类别

五个比较类别被定义为 std:: 类型,每个都有以下预定义值:

+--------------------------------------------------------------------+
|                  |          Numeric  values          | Non-numeric |
|     Category     +-----------------------------------+             |
|                  | -1   | 0          | +1            |   values    |
+------------------+------+------------+---------------+-------------+
| strong_ordering  | less | equal      | greater       |             |
| weak_ordering    | less | equivalent | greater       |             |
| partial_ordering | less | equivalent | greater       | unordered   |
| strong_equality  |      | equal      | nonequal      |             |
| weak_equality    |      | equivalent | nonequivalent |             |
+------------------+------+------------+---------------+-------------+

这些类型之间的隐式转换定义如下:

    具有值 {lessequalgreater} 的
  • strong_ordering 隐式转换为:
    • weak_ordering,值为 {lessequivalentgreater}
    • partial_ordering,值为 {lessequivalentgreater}
    • strong_equality,值为 {unequalequalunequal}
    • weak_equality,值为 {nonequivalentequivalentnonequivalent}
  • 具有值 {lessequivalentgreater} 的
  • weak_ordering 隐式转换为:
    • partial_ordering,值为 {lessequivalentgreater}
    • weak_equality,值为 {nonequivalentequivalentnonequivalent}
  • 具有值 {lessequivalentgreaterunordered} 的
  • partial_ordering 隐式转换为:
    • weak_equality 的值为 {nonequivalentequivalentnonequivalentnonequivalent}
  • strong_equality 与值 {equal, unequal} 隐式转换为:
    • weak_equality 值 {equivalent, nonequivalent}

三路比较

引入了<=>令牌。在旧源代码中,字符序列<=>标记为<= >。例如,X<&Y::operator<=>需要加一个space来保留其意义。

可重载运算符<=>是一个三向比较函数,优先级高于<低于<<。它 return 是一种可以与文字 0 进行比较的类型,但允许使用其他 return 类型,例如支持表达式模板。在语言和标准库中定义的所有<=>运算符return上述5种std::比较类别类型之一。

对于语言类型,提供了以下内置<=>同类型比较。所有都是 constexpr,除非另有说明。不能使用标量 promotions/conversions.

异构调用这些比较
  • 对于bool、整数和指针类型,<=>returnsstrong_ordering
  • 对于指针类型,允许不同的cv-qualifications和derived-to-base转换调用一个homogeneous built-in<=>,并且还有heterogeneous built-inoperator<=>(T*, nullptr_t)。只有指向相同 object/allocation 的指针的比较才是常量表达式。
  • 对于基本浮点类型,<=> returnspartial_ordering,并且可以通过将参数扩展到更大的浮点类型来异构调用。
  • 对于枚举,<=> return与枚举的基础类型相同<=>
  • 对于nullptr_t,<=> returnsstrong_ordering并且总是产生equal.
  • 对于可复制数组,T[N] <=> T[N]return与T<=>的类型相同,并执行字典顺序元素比较。其他数组没有<=>
  • 对于void没有<=>.

为了更好地理解这个运算符的内部工作原理,请阅读原文paper。这正是我使用搜索引擎发现的。

这称为三向比较运算符。

根据 P0515 论文提案:

There’s a new three-way comparison operator, <=>. The expression a <=> b returns an object that compares <0 if a < b, compares >0 if a > b, and compares ==0 if a and b are equal/equivalent.

To write all comparisons for your type, just write operator<=> that returns the appropriate category type:

  • Return an _ordering if your type naturally supports <, and we’ll efficiently generate <, >, <=, >=, ==, and !=; otherwise return an _equality, and we’ll efficiently generate == and !=.

  • Return strong if for your type a == b implies f(a) == f(b) (substitutability, where f reads only comparison-salient state accessible using the nonprivate const interface), otherwise return weak.

cppreference 说:

The three-way comparison operator expressions have the form

lhs <=> rhs   (1)  

The expression returns an object that

  • compares <0 if lhs < rhs
  • compares >0 if lhs > rhs
  • and compares ==0 if lhs and rhs are equal/equivalent.

由于引用的网页已更改,此答案已变得无关紧要

web page you are referencing 坏了。那天它被编辑了很多,不同的部分不同步。我看的时候状态是:

在页面顶部,它列出了当前存在的比较运算符(在 C++14 中)。那里没有<=>

在页面底部,他们本应列出相同的运算符,但他们犯了错误并添加了这个未来的建议。

gcc 还不知道 <=>-std=c++14,永远不会),所以它认为你的意思是 a <= > b。这解释了错误消息。

如果你在五年后尝试同样的事情,你可能会得到更好的错误信息,比如 <=> not part of C++14.

默认 <=> 自动免费提供 ==, !=, <, >, <=, >=

C++20 有一个新的“默认比较”功能设置,因此默认 <=> 可以免费提供所有其他功能。我相信这是添加 operator<=>.

背后的主要动机

改编自https://en.cppreference.com/w/cpp/language/default_comparisons

main.cpp

#include <cassert>
#include <compare>
#include <set>

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Just to show it Is enough for `std::set`.
    std::set<Point> s;
    s.insert(pt1);

    // All of these are automatically defined for us!
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
    assert( (pt1 <  pt2));
    assert( (pt1 <= pt2));
    assert(!(pt1 >  pt2));
    assert(!(pt1 >= pt2));
}

编译并运行:

sudo apt install g++-10
g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

上面的一个等效的更明确的版本是:

struct Point {
    int x;
    int y;
    auto operator<=>(const Point& other) const {
        if (x < other.x) return -1;
        if (x > other.x) return 1;
        if (y < other.y) return -1;
        if (y > other.y) return 1;
        return 0;
    }
    bool operator==(const Point& other) const = default;
};

在这种情况下,我们需要显式设置 bool operator==(const Point& other) const = default;,因为如果 operator<=> 未默认(例如,如上所述),则 operator== 不会自动默认:

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<=> 相同的算法,如 cppreference 所解释的那样:

The default operator<=> performs lexicographical comparison by successively comparing the base (left-to-right depth-first) and then non-static member (in declaration order) subobjects of T to compute <=>, recursively expanding array members (in order of increasing subscript), and stopping early when a not-equal result is found

在 C++20 之前,您不能做类似 operator== = default 的事情,并且定义一个运算符不会导致定义其他运算符,例如以下无法使用 -std=c++17 编译:

#include <cassert>

struct Point {
    int x;
    int y;
    auto operator==(const Point& other) const {
        return x == other.x && y == other.y;
    };
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Do some checks.
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
}

有错误:

main.cpp:16:18: error: no match for ‘operator!=’ (operand types are ‘Point’ and ‘Point’)
   16 |     assert( (pt1 != pt2));
      |              ~~~ ^~ ~~~
      |              |      |
      |              Point  Point

上面的代码确实在 -std=c++20 下编译。

相关:

在 Ubuntu 20.04、GCC 10.2.0 上测试。

C++ 20 中引入了三向比较运算符 (<=>)。

这个表达式returns对象如下;

auto cmp  = a <=> b;

cmp > 0 if a > b
cmp = 0 if a == b
cmp < 0 if a < b  

示例程序

#include <iostream>

using namespace std;

int main()
{
        int lhs = 10, rhs = 20;
        auto result = lhs <=> rhs;

        if (result < 0) {
                cout << "lhs is less than rhs" << endl;
        }
        else if (result > 0) {
                cout << "lhs is greater than rhs" << endl;
        }
        else {
                cout << "lhs and rhs are equal" << endl;
        }

}

如何编译和运行?

g++-10 threewaycmp.cpp -std=c++20
./a.out

结果

lhs is less than rhs

详情请参考下方link https://en.cppreference.com/w/cpp/language/operator_comparison