253

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

在此处输入图像描述

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

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

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

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

4

5 回答 5

201

这称为三向比较运算符

根据P0515论文提案:

有一个新的三路比较运算符,<=>. 该表达式a <=> b返回一个比较<0if a < b、比较>0ifa > b和比较==0ifab是否相等/等价的对象。

要为您的类型编写所有比较,只需编写operator<=>返回适当的类别类型:

  • 如果您的类型自然支持,则返回_ordering<,我们将有效地生成<, >, <=, >=,==!=; 否则返回_equality,我们将有效地生成 ==!=

  • 如果您的类型a == b暗示f(a) == f(b)(可替代性,其中 f 仅读取使用非私有 const 接口可访问的比较显着状态),则返回强,否则返回弱。

cppreference说:

三向比较运算符表达式具有以下形式

lhs <=> rhs   (1)  

表达式返回一个对象

  • 比较<0如果lhs < rhs
  • 比较>0如果lhs > rhs
  • 并比较和==0是否相等/等价。lhsrhs
于 2017-11-24T04:38:40.087 回答
134

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} 隐式转换为:
    • weak_ordering具有值 { less, equivalent, greater}
    • partial_ordering具有值 { less, equivalent, greater}
    • 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}
  • partial_ordering使用值 { less, equivalent, greater, unordered} 隐式转换为:
    • weak_equality具有值 { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equality使用值 { equal, unequal} 隐式转换为:
    • weak_equality有值 { equivalent, nonequivalent}

三路比较

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

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

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

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

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

于 2017-11-24T04:32:57.007 回答
12

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

您引用的网页已损坏。那天编辑了很多,不同的部分不同步。我看的时候的状态是:

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

在页面底部,他们应该列出相同的运营商,但他们搞砸了并添加了这个未来的建议。

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

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

于 2017-11-24T08:36:17.133 回答
11

违约<=>自动==, !=, <, >, <=, >=免费赠送

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==不会自动默认:

根据任何重载的规则operator<=>,默认<=>重载还允许将类型与<<=>和进行比较>=

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

上面的示例使用与 default 相同的算法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 上测试。

于 2020-11-10T15:46:27.410 回答
2

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

该表达式返回如下对象;

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

有关详细信息,请参阅以下链接 https://en.cppreference.com/w/cpp/language/operator_comparison

于 2021-07-10T06:52:34.640 回答