C++20新特性_三路比较运算符 (<=>)

文章目录

  • [第一章 C++20核心语法特性](#第一章 C++20核心语法特性)
    • [1.5 三路比较运算符 (<=>)](#1.5 三路比较运算符 (<=>))

本文记录C++20新特性之三路比较运算符 (<=>)。

第一章 C++20核心语法特性

1.5 三路比较运算符 (<=>)

在C++11/14/17中,如果定义一个支持所有比较运算符的类,需要手动重载6个运算符,如下:

cpp 复制代码
    struct Point {
        int x, 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 true;
            if (other.x < x) return false;
            return y < other.y;
        }
        bool operator>( const Point& other) const { return other < *this; }
        bool operator<=(const Point& other) const { return !(*this > other); } // 不大于
        bool operator>=(const Point& other) const { return !(*this < other); } // 不小于
    };

    void test()
    {
        Point p1{ 10,20 };
        Point p2{ 15,25 };
        if (p1 < p2)
		{
			std::cout << "p1 小于 p2" << std::endl;
        }
        if (p1 != p2)
        {
            std::cout << "p1 不等于 p2" << std::endl;
        }
        /*
            p1 小于 p2
            p1 不等于 p2
        */
    }

C++20引入了三路比较运算符,让上面的代码实现更简洁。

1.5.1 实现原理

三路比较运算符 <=> 返回的不是 bool,而是一个表示"顺序关系"的对象。

这三种顺序关系如下:

1 std::strong_ordering,表示强序。如果 a == b,则它们在任何情况下都是可互换的。

适用:int, char, 指针等。

值:less, equal, greater。

2 std::weak_ordering:表示弱序。如果 a == b(等价),它们可能仍然有区别(例如大小写不敏感的字符串比较,"A" 和 "a" 等价,但它们的数据不同)。

适用:大小写不敏感字符串。

值:less, equivalent, greater。

3 std::partial_ordering,表示偏序。某些值可能无法比较(例如浮点数中的 NaN,它既不小于、也不大于、也不等于任何数)。

适用:float, double。

值:less, equivalent, greater, unordered。

编译器的重写规则:当写a < b时,编译器会尝试查找 operator<,如果没有找到,会查找 operator<=>,将代码重写为 (a<=> b) < 0

1.5.2 使用举例

示例1: 自动生成比较运算符

下面Point类支持比较运算符,可以直接operator<=>:

cpp 复制代码
    class Point
    {
    public:
        int x;
		int y;

		// 一行代码,自动生成 == , != , < , <= , > , >= 运算符重载
		auto operator<=>(const Point& other) const = default;
    };

    void test()
    {
        Point p1{ 10,20 };
        Point p2{ 15,25 };
        if (p1 < p2) // 编译器自动转换为 (p1 <=> p2) < 0
        {
			std::cout << "p1 小于 p2" << std::endl;
        }

        if (p1 != p2) // 编译器自动转换为 (p1 <=> p2) != 0
        {
			std::cout << "p1 不等于 p2" << std::endl;
        }
    }
示例2:自定义比较逻辑

假设我们有一个类,比较string类型的name, 并忽略大小写。

cpp 复制代码
    struct User
    {
        int id;
        std::string name;

        std::weak_ordering operator<=>(const User& obj) const
        {
            // 逐字符比较,忽略大小写
            std::string s1 = name;
            std::string s2 = obj.name;
            for (auto& c : s1) c = std::tolower(c);
            for (auto& c : s2) c = std::tolower(c);

            // string 的 <=> 返回 strong_ordering,我们可以隐式转换为 weak_ordering
            if (auto cmp = s1 <=> s2; cmp != 0) 
            {
                return cmp;
            }
            return std::weak_ordering::equivalent;
        }

        // 自定义了 <=>后,默认显示
        bool operator==(const User& obj) const
        {
            return (*this <=> obj) == 0;
        }
    };

    void test()
    {
        User u1{ 1, "Alice" };
        User u2{ 99, "alice" };

        if (u1 == u2)
        {
            std::cout << "相等" << endl;
        }
        // 相等
    }
示例3:浮点数陷阱

由于浮点数存在 NaN,只能使用 partial_ordering。

cpp 复制代码
    struct Data {
        double value;

        // 必须使用 partial_ordering,因为 double 只能偏序比较
        std::partial_ordering operator<=>(const Data& other) const {
            return value <=> other.value;
        }

        bool operator==(const Data&) const = default;
    };

    void test()
    {
        Data d1{ 3.14 };
        Data d2{ 2.71 };
        Data d3{ std::nan("") }; // NaN 值
        if (auto cmp = d1 <=> d2; cmp == std::partial_ordering::greater) 
        {
            std::cout << "d1 大于 d2" << std::endl;
        }
        if (auto cmp = d1 <=> d3; cmp == std::partial_ordering::unordered) 
        {
            std::cout << "d1 和 d3 无法比较 (NaN)" << std::endl;
        }
        /*
            d1 大于 d2
            d1 和 d3 无法比较 (NaN)
		*/
    }
相关推荐
Mr_WangAndy1 小时前
C++20新特性_consteval 和 constinit
c++20·c++40周年·consteval·constinit
Mr_WangAndy2 小时前
C++20新特性_协程(Coroutines)
c++20·c++20新特性·c++40周年·c++20协程
Mr_WangAndy2 小时前
C++20新特性_Lambda 改进
c++20·c++20新特性·c++40周年·lambda表达式改进
Mr_WangAndy11 小时前
C++17 新特性_第二章 C++17 语言特性_std::any和string_view
c++·string_view·c++40周年·c++17新特性·c++新特性any
Mr_WangAndy14 小时前
C++17 新特性_第一章 C++17 语言特性___has_include,u8字符字面量
c++·c++40周年·c++17新特性·__has_include·u8字面量
Mr_WangAndy1 天前
C++17 新特性_第二章 C++17标准库特性_std::invoke和std::invoke_result_t
c++·invoke·c++40周年·c++17新特性·invoke_result
Mr_WangAndy1 天前
C++14 新特性_第二章 C++14 标准库特性_std::exchange,std::quoted
c++·exchange·c++40周年·quoted·c++14新特性
Mr_WangAndy1 天前
C++17 新特性_第一章 C++17 语言特性_if constexpr,类模板参数推导 (CTAD)
c++·c++40周年·if constexpr·类模板参数推导 ctad·c++17新特性
止观止2 天前
告别“祖传C++”:开启你的现代C++之旅
c++·c++11·c++20·编程思想·现代c++