什么是<=>("太空船",三向比较)操作符在c++ ?

当我试图学习c++操作符时,我在一个表中偶然发现了cppreference.com上的一个奇怪的比较操作符,它看起来像这样:

enter image description here

“好吧,如果这些是c++中常见的运算符,我最好学习一下”,我想。但是我所有想要阐明这个秘密的尝试都没有成功。即使在Stack Overflow上,我的搜索也没有运气。

& lt; =比;c++之间有联系吗?

如果有,这个算子具体做什么?

*与此同时cppreference.com更新了该页面,现在包含有关__abc0operator的信息。

78498 次浏览

2017-11-11上,ISO c++委员会采纳了Herb Sutter<=>“太空船”三向比较运算符的建议,作为添加到C + + 20的新特性之一。在题为一致的比较 Sutter的论文中,Maurer和Brown演示了新设计的概念。关于该提案的概述,以下是文章的摘录:

表达式b <=>返回一个比较& lt; 0的对象,如果a < b,如果A > b则比较> 0,如果a和b为,则比较= = 0 平等/等价的。< / p >

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

auto X::operator<=>(const Y&) =default;
要编写你的类型X与类型Y的所有比较,只需编写operator< = >,它接受Y,可以使用 =违约来获取成员语义(如果需要的话),并返回 合适的类别类型:

  • 如果您的类型自然支持& lt;,则返回_ordering,我们将有效地生成对称的& lt;>& lt; => == =! =;否则返回_equality,我们将有效地生成 对称的= =! =.
  • 如果对于你的类型,A == b暗示F (a) == F (b)(可替换性,其中f仅读取比较显着状态,则返回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 |             |
+------------------+------+------------+---------------+-------------+

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

  • 带有值{lessequalgreater}的strong_ordering隐式转换为:
    • weak_ordering的值为{lessequivalentgreater}
    • partial_ordering的值为{lessequivalentgreater}
    • strong_equality的值为{unequalequalunequal}
    • weak_equality的值为{nonequivalentequivalentnonequivalent}
    • 李< / ul > < / >
    • 带有值{lessequivalentgreater}的weak_ordering隐式转换为:
      • partial_ordering的值为{lessequivalentgreater}
      • weak_equality的值为{nonequivalentequivalentnonequivalent}
      • 李< / ul > < / >
      • 带有值{lessequivalentgreaterunordered}的partial_ordering隐式转换为:
        • weak_equality的值为{nonequivalentequivalentnonequivalentnonequivalent}
        • 李< / ul > < / >
        • strong_equality带值{equalunequal}隐式转换为:
          • weak_equality的值为{equivalentnonequivalent}
          • 李< / ul > < / >

          三方比较

          引入__abc0token。在旧源代码中,字符序列__abc0tokeniziz__abc2。例如,__abc3需要添加一个空格来保留其含义。

          可重载操作符__abc0是一个三向比较函数,其优先级高于__abc1,低于__abc2。它返回一个可以与literal__abc3比较的类型,但也允许返回其他类型,例如支持表达式模板。在语言和标准库中定义的所有__abc0操作符都会返回上述5种__abc5比较类别类型之一。

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

          • 对于__abc0、整型和指针类型,__abc1返回__abc2。
          • 对于指针类型,不同的cv- qualifiers和derived-to-base转换允许调用同构的buildenin__abc0,并且有内置的heterogeneousoperator<=>(T*, nullptr_t)。只有指向同一对象/分配的指针的比较才是常量表达式。
          • 对于基本浮点类型,<=>返回__abc1,并且可以通过将参数扩展为更大的浮点类型以异构方式调用。
          • 对于枚举,<=>返回与枚举基础类型的<=>相同的值。
          • 对于__abc0,<=>返回__abc2,并且总是返回__abc3。
          • 对于可复制的数组,__abc0返回与__abc1的__abc2相同的类型,并执行按字典元素的比较。其他数组没有__abc2。
          • 对于__abc0,没有__abc1。

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

这被称为三方比较操作符。

根据P0515论文提案:

有一个新的三向比较运算符<=>。表达式a <=> b返回一个对象,如果a < b则比较<0,如果a > b则比较>0,如果ab相等/等效则比较==0

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

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

  • 返回strong if对于你的类型a == b暗示f(a) == f(b)(可替换性,其中f仅读取比较显著状态 使用非私有const接口访问),否则返回 李弱。< / p > < / >

cppreference表示:

三向比较运算符表达式的形式为

lhs <=> rhs   (1)

表达式返回一个对象

  • 如果lhs < rhs,则比较<0
  • 如果lhs > rhs,则比较>0
  • 如果lhsrhs相等/等效,则比较==0

这个答案已经变得无关紧要,因为引用的网页已经改变了

您正在引用的网页坏了。那天它被编辑了很多次,不同的部分不同步。我当时看到的状态是:

在页面的顶部,它列出了当前存在的比较操作符(在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==不会自动默认值:

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

如果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上测试。

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 < / p >

<=>用于复杂类型,其比较开销很大,例如树导航。你可以" x = A <=>B;“然后从x中确定>, <, >=, <=, ==, !=,而不需要对A和b进行额外的比较。对于树,想象bool find(root, A){如果root为nullptr返回false;int x = A <=>根→B;If !x,你发现它返回true,否则如果x <0,找到左,否则找到右}。(对尾递归进行调优。)

我以为有些语言有一个三向控制流,像开关,但很难通过谷歌回忆。这种情况可以追溯到C的strcmp(), memcmp(),以及JAVA的compareTo()。众包我!