什么是 C 中的 <=>(“宇宙飞船”,三向比较)运算符?

新手上路,请多包涵

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

在此处输入图像描述

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

<=>C++ 之间有联系吗?

如果有,这个操作员究竟做了什么?

\* 与此同时,cppreference.com 更新了该页面,现在包含有关 <=> 运算符的信息。

原文由 q-l-p 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

2017 年 11 月 11 日,ISO C++ 委员会采纳了 Herb Sutter 关于 <=> “宇宙飞船” 三路比较运算符 的提议,作为添加到 C++20 的新特性之一。在题为 一致比较 Sutter、Maurer 和 Brown 的论文中,展示了新设计的概念。有关该提案的概述,这里是文章的摘录:

表达式 a <=> b 返回一个对象,如果 a < b 比较 <0 ,如果 a > b 比较 >0 ,如果 a 和 b 相等/等价,则比较 ==0

常见情况: 要编写类型 X 与类型 Y 的所有比较,使用成员语义,只需编写:

>  auto X::operator<=>(const Y&) =default;
>
> ```
>
> **高级案例:** 要编写类型 **X** 与类型 **Y** 的所有比较,只需编写带有 **Y** 的 **operator<=>** ,如果需要,可以使用 **=default** 获取成员语义,并返回适当的类别类型:
>
> - 如果您的类型自然支持 **<** ,则返回 **_ordering** ,我们将有效地生成对称 **<** 、 **>** 、 **<=** 、 **>=** 、 **==** 和 **!=** ;否则返回 **_equality** ,我们将有效地生成对称 **==** 和 **!=** 。
> - 如果您的类型 **a == b** 意味着 **f(a) == f(b)** (可替换性,其中 **f** 仅读取可使用公共 **const** 成员访问的比较显着状态),则返回 **strong_** ,否则返回 **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 | | +——————+——+————+—————+————-+

”`

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

  • strong_ordering 具有值 { less , equal , greater 隐式转换为:}7
    • weak_ordering 有值 { less , equivalent , greater
    • partial_ordering 具有值 { less , equivalent , greater }ea
    • strong_equality 带有值 { unequal , equal , unequal
    • weak_equality 带有值 { nonequivalent , equivalent , nonequivalent } —
  • weak_ordering 具有值 { less , equivalent , greater
    • partial_ordering 带有值 { less , equivalent , greater
    • weak_equality 具有值 { nonequivalent , equivalent , nonequivalent }b
  • partial_ordering with values { less , equivalent , greater , unordered } implicitly converts to:
    • weak_equality with values { nonequivalent , equivalent , nonequivalent , nonequivalent }
  • strong_equality 具有值 { equal , unequal } 隐式转换为:
    • weak_equality 具有值 { equivalent , nonequivalent }

三路比较

引入了 <=> 令牌。旧源代码中的字符序列 <=> 标记为 <= > 。比如 X<&Y::operator<=> 需要加一个空格来保留其含义。

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

对于语言类型,提供了以下内置的 <=> 同类型比较。所有都是 constexpr ,除非另有说明。不能使用标量提升/转换异构调用这些比较。

  • 对于 bool 、整数和指针类型, <=> 返回 strong_ordering
  • 对于指针类型,允许不同的 cv 限定和派生到基的转换调用同构的内置 <=> ,并且有内置的异构 operator<=>(T*, nullptr_t) 。只有指向相同对象/分配的指针的比较才是常量表达式。
  • 对于基本浮点类型, <=> 返回 partial_ordering ,并且可以通过将参数扩展为更大的浮点类型来异构调用。
  • 对于枚举, <=> 返回与枚举的基础类型相同的 <=>
  • 对于 nullptr_t<=> 返回 strong_ordering 并且总是产生 equal
  • 对于可复制数组, T[N] <=> T[N] 返回与 T<=> 相同的类型并执行字典元素比较。其他阵列没有 <=>
  • 对于 void 没有 <=>

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

原文由 q-l-p 发布,翻译遵循 CC BY-SA 4.0 许可协议

默认 <=> 自动给 ==, !=, <, >, <=, >= 免费

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

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

主文件

#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 <=> overload will also allow the type to be compared with < , <= , >>=

如果 operator<=> 是默认的并且 operator== 根本没有声明,那么 operator== 是隐式默认的。

上面的示例使用与默认相同的算法 operator<=> ,如 cppreference 所述:

默认 operator<=> 通过依次比较 T 的基数(从左到右深度优先)和非静态成员(按声明顺序)子对象来计算 <=>,递归扩展数组来执行字典顺序比较成员(按下标递增的顺序),并在发现不相等的结果时提前停止

在 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 下编译。

相关: 是否有任何 C++ 运算符重载基于其他运算符自动提供?

在 Ubuntu 20.04、GCC 10.2.0 上测试。

原文由 Ciro Santilli OurBigBook.com 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏