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 | |
+------------------+------+------------+---------------+-------------+
这些类型之间的隐式转换定义如下:
具有值 {less
、equal
、greater
} 的 strong_ordering
隐式转换为:
weak_ordering
,值为 {less
、equivalent
、greater
}
partial_ordering
,值为 {less
、equivalent
、greater
}
strong_equality
,值为 {unequal
、equal
、unequal
}
weak_equality
,值为 {nonequivalent
、equivalent
、nonequivalent
}
具有值 {less
、equivalent
、greater
} 的 weak_ordering
隐式转换为:
partial_ordering
,值为 {less
、equivalent
、greater
}
weak_equality
,值为 {nonequivalent
、equivalent
、nonequivalent
}
具有值 {less
、equivalent
、greater
、unordered
} 的 partial_ordering
隐式转换为:
weak_equality
的值为 {nonequivalent
、equivalent
、nonequivalent
、nonequivalent
}
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
当我尝试学习 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 | |
+------------------+------+------------+---------------+-------------+
这些类型之间的隐式转换定义如下:
-
具有值 {
strong_ordering
隐式转换为:weak_ordering
,值为 {less
、equivalent
、greater
}partial_ordering
,值为 {less
、equivalent
、greater
}strong_equality
,值为 {unequal
、equal
、unequal
}weak_equality
,值为 {nonequivalent
、equivalent
、nonequivalent
}
具有值 {weak_ordering
隐式转换为:partial_ordering
,值为 {less
、equivalent
、greater
}weak_equality
,值为 {nonequivalent
、equivalent
、nonequivalent
}
具有值 {partial_ordering
隐式转换为:weak_equality
的值为 {nonequivalent
、equivalent
、nonequivalent
、nonequivalent
}
strong_equality
与值 {equal
,unequal
} 隐式转换为:weak_equality
值 {equivalent
,nonequivalent
}
less
、equal
、greater
} 的 less
、equivalent
、greater
} 的 less
、equivalent
、greater
、unordered
} 的 三路比较
引入了<=>
令牌。在旧源代码中,字符序列<=>
标记为<= >
。例如,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 expressiona <=> b
returns an object that compares<0
ifa < b
, compares>0
ifa > b
, and compares==0
ifa
andb
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
impliesf(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
iflhs < rhs
- compares
>0
iflhs > rhs
- and compares
==0
iflhs
andrhs
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 andoperator==
is not declared at all, thenoperator==
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