跳转到内容

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`)会按成员字典序生成比较逻辑。
  • 适用于排序、容器键值等场景,显著减少样板代码。

参见[编辑 | 编辑源代码]