C++20 引入的三路比较运算符(three-way comparison operator),也被称为"宇宙飞船运算符"(spaceship operator),写作 <=>。用于简化和统一比较操作。
基本概念
<=> 运算符一次性完成两个值的比较,返回三种可能的结果:
- 小于(less)- (a <=> b) < 0 if a < b
- 等于(equal)- (a <=> b) == 0 if a and b are equal/equivalent.
- 大于(greater)- (a <=> b) > 0 if a > b
返回类型
它返回一个表示比较结果的值,可以直接用于排序和相等性判断。
<=> 返回以下三种类型之一:
std::strong_ordering - 强排序
- 值:
less, equal, greater - 适用于:整数、指针等
std::weak_ordering - 弱排序
- 值:
less, equivalent, greater - 适用于:大小写不敏感的字符串比较
std::partial_ordering - 偏序
- 值:
less, equivalent, greater, unordered - 适用于:浮点数(因为有 NaN)
动机
当第一次接触到<=> 运算符时,大概会比较困惑,它的作用到底是什么?使用场景又是什么?等等一系列问题。
在C++ 20之前,我们如果要比较自定义类型,通常需要这样做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| struct Point {
int x, y;
bool operator==(const Point& rhs) const {
return x == rhs.x && y == rhs.y;
}
bool operator!=(const Point& rhs) const {
return !(*this == rhs);
}
bool operator<(const Point& rhs) const {
if (x != rhs.x)
return x < rhs.x;
return y < rhs.y;
}
bool operator<=(const Point& rhs) const {
return !(rhs < *this);
}
bool operator>(const Point& rhs) const {
return rhs < *this;
}
bool operator>=(const Point& rhs) const {
return !(*this < rhs);
}
};
int main() {
Point p1{1, 2}, p2{1, 3};
std::cout << std::boolalpha << (p1 < p2) << '\n'; // true
std::cout << std::boolalpha << (p1 == p2) << "\n"; // false
}
|
即,需要实现 6 个比较运算符,才能进行自定义类型的比较。而定义一个 <=> 运算符后,编译器会自动生成所有六个比较运算符(<, <=, >, >=, ==, !=)
基本用法
基本类型比较:
1
2
3
4
5
6
7
8
| int main() {
int a = 5, b = 10;
std::strong_ordering result = a <=> b;
if (result < 0) {
std::cout << "a < b\n";
}
}
|
自定义类型比较:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include <compare>
#include <iostream>
struct Point {
int x, y;
// 只需定义 <=> 运算符
auto operator<=>(const Point& other) const = default;
};
int main() {
Point p1{1, 2}, p2{1, 3};
if (p1 < p2) {
std::cout << "p1 < p2\n";
}
return 0;
}
|
自定义比较逻辑 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| struct Person
{
std::string name;
int age;
// 先比较年龄,相同再按姓名
auto operator<=>(const Person& rhs) const
{
if (auto cmp = age <=> rhs.age; cmp != 0)
return cmp;
return name <=> rhs.name;
}
// 仍需显式定义 ==
bool operator==(const Person& rhs) const = default;
};
Person p1{"Alice", 30}, p2{"Bob", 25};
std::cout << std::boolalpha << (p1 >= p2) << '\n'; // true (30 > 25)
std::cout << std::boolalpha << (p1 == p2) << '\n'; // false (30 != 25)
|
[!note]
当自定义了 operator<=> 后,需要显示定义 opretor== 运算符,因为 operator<=> 不会自动生成 operator==,主要是为了性能考虑,<=> 可能做了不必要的排序比较,而 == 只需要判断相等即可。
官方解释:
类型有了 <=> 之后,== 是从 <=> 生成的。对于字符串,== 通常通过首先比较大小来优化:如果字符数不同,则字符串不相等。从 <=> 生成的 == 则必须读取足够的字符串以确定它们的词典顺序,那开销就会大得多了。经过长时间的讨论,我们决定不从 <=> 生成 ==。 - 源自《C++ 白皮书》
总结
简化代码:定义一个 <=> 运算符后,编译器自动生成所有六个比较运算符(<, <=, >, >=, ==, !=)
性能优化:一次比较得到完整的排序关系,避免多次比较
默认实现:使用 = default 可以让编译器自动生成按成员字典序比较的实现
https://www.stroustrup.com/hopl20main-p5-p-bfc9cd4--final.pdf
https://github.com/Cpp-Club/Cxx_HOPL4_zh