C++20 三路比较
C++20 三路比较(Three-way comparison),也称为宇宙飞船运算符(Spaceship Operator),是 C++20 引入的一项重要特性,旨在简化比较运算符的实现并提高代码的可读性和效率。它通过引入一个新的运算符 `<=>` 来统一处理所有可能的比较结果。
概述[编辑 | 编辑源代码]
在 C++20 之前,如果要为一个自定义类型实现完整的比较功能(如 `<`、`<=`、`>`、`>=`、`==` 和 `!=`),通常需要手动重载多个运算符,这不仅繁琐,还容易引入不一致性。C++20 的三路比较运算符 `<=>` 解决了这一问题,它返回一个比较类别(Comparison Category)对象,表示两个值的相对顺序关系。
三路比较运算符返回的结果类型可以是以下之一:
- `std::strong_ordering`:表示强序关系(严格的全序)。
- `std::weak_ordering`:表示弱序关系(允许等价但不完全相等的值)。
- `std::partial_ordering`:表示偏序关系(允许不可比较的值)。
- `std::strong_equality` 和 `std::weak_equality`(在 C++20 中已弃用)。
语法与基本用法[编辑 | 编辑源代码]
三路比较运算符 `<=>` 的语法如下:
auto operator<=>(const T& other) const;
示例:基本三路比较[编辑 | 编辑源代码]
以下是一个简单的示例,展示如何为自定义类型实现 `<=>` 运算符:
#include <compare>
#include <iostream>
struct Point {
int x;
int y;
// 实现三路比较运算符
auto operator<=>(const Point& other) const = default;
};
int main() {
Point p1{1, 2};
Point p2{1, 3};
if (p1 < p2) {
std::cout << "p1 is less than p2\n";
} else if (p1 > p2) {
std::cout << "p1 is greater than p2\n";
} else {
std::cout << "p1 is equal to p2\n";
}
}
输出:
p1 is less than p2
解释[编辑 | 编辑源代码]
1. `Point` 结构体定义了两个成员变量 `x` 和 `y`。 2. 通过 `auto operator<=>(const Point& other) const = default;` 使用默认实现的三路比较运算符。 3. 编译器会自动生成 `==`、`!=`、`<`、`<=`、`>` 和 `>=` 运算符,基于 `x` 和 `y` 的字典序比较。
比较类别[编辑 | 编辑源代码]
三路比较运算符的返回值类型决定了比较的语义:
`std::strong_ordering`[编辑 | 编辑源代码]
表示强序关系,通常用于可完全排序的类型(如整数、浮点数等)。可能的返回值:
- `std::strong_ordering::less`(表示 `a < b`)
- `std::strong_ordering::equal`(表示 `a == b`)
- `std::strong_ordering::greater`(表示 `a > b`)
`std::weak_ordering`[编辑 | 编辑源代码]
表示弱序关系,允许等价但不完全相等的值(如大小写不敏感的字符串比较)。可能的返回值:
- `std::weak_ordering::less`
- `std::weak_ordering::equivalent`
- `std::weak_ordering::greater`
`std::partial_ordering`[编辑 | 编辑源代码]
表示偏序关系,允许不可比较的值(如浮点数中的 `NaN`)。可能的返回值:
- `std::partial_ordering::less`
- `std::partial_ordering::equivalent`
- `std::partial_ordering::greater`
- `std::partial_ordering::unordered`(表示不可比较)
自定义三路比较实现[编辑 | 编辑源代码]
如果默认的比较行为不满足需求,可以手动实现 `<=>` 运算符。例如:
#include <compare>
#include <iostream>
struct Person {
std::string name;
int age;
// 自定义三路比较:先按年龄,再按名字
auto operator<=>(const Person& other) const {
if (auto cmp = age <=> other.age; cmp != 0) {
return cmp;
}
return name <=> other.name;
}
};
int main() {
Person p1{"Alice", 30};
Person p2{"Bob", 25};
if (p1 > p2) {
std::cout << "p1 is greater than p2\n";
} else {
std::cout << "p1 is not greater than p2\n";
}
}
输出:
p1 is greater than p2
解释[编辑 | 编辑源代码]
1. `Person` 结构体定义了 `name` 和 `age` 成员。 2. 自定义的 `<=>` 先比较 `age`,如果年龄相同,再比较 `name`。 3. 返回值类型为 `std::strong_ordering`(因为 `int` 和 `std::string` 的比较都是强序的)。
实际应用场景[编辑 | 编辑源代码]
三路比较在以下场景中非常有用: 1. **排序算法**:简化自定义类型的比较逻辑,便于在 `std::sort` 或 `std::set` 中使用。 2. **容器键值**:为 `std::map` 或 `std::unordered_map` 的键类型提供一致的比较。 3. **性能优化**:避免重复比较(如先比较 `a == b`,再比较 `a < b`)。
示例:在 `std::sort` 中使用[编辑 | 编辑源代码]
#include <algorithm>
#include <vector>
#include <iostream>
struct Book {
std::string title;
int year;
auto operator<=>(const Book& other) const = default;
};
int main() {
std::vector<Book> books{
{"The C++ Programming Language", 2013},
{"Effective Modern C++", 2014},
{"A Tour of C++", 2018}
};
std::sort(books.begin(), books.end());
for (const auto& book : books) {
std::cout << book.title << " (" << book.year << ")\n";
}
}
输出:
A Tour of C++ (2018) Effective Modern C++ (2014) The C++ Programming Language (2013)
解释[编辑 | 编辑源代码]
1. `Book` 结构体定义了 `title` 和 `year` 成员。 2. 使用 `= default` 生成默认的三路比较运算符,按成员字典序比较。 3. `std::sort` 使用 `<=>` 对书籍按标题排序。
与旧版 C++ 的比较[编辑 | 编辑源代码]
在 C++20 之前,实现完整的比较运算符需要手动重载多个运算符,例如:
struct Point {
int x;
int y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
bool operator!=(const Point& other) const {
return !(*this == other);
}
bool operator<(const Point& other) const {
if (x != other.x) return x < other.x;
return y < other.y;
}
// 还需要重载 <=、>、>=
};
C++20 的三路比较大大简化了这一过程。
总结[编辑 | 编辑源代码]
- 三路比较运算符 `<=>` 是 C++20 引入的重要特性,用于统一比较逻辑。
- 返回值类型可以是 `std::strong_ordering`、`std::weak_ordering` 或 `std::partial_ordering`。
- 默认实现(`= default`)会按成员字典序生成比较逻辑。
- 适用于排序、容器键值等场景,显著减少样板代码。