跳转至

第一章 比较运算符<=>

目录

0. 环境配置

# gcc 13.x
sudo apt install gcc g++ gdb
# 安装fmt库
sudo apt install libfmt-dev

1. 比较运算符

背景: 主要是为了解决cpp一个类需要进行比较时, 需要定义六个操作符

1.1 介绍

  • cpp20+, 不再需要自己单独定义==!=这两个操作符, 只需要定义==即可, 编译器会帮我们重写并查找!(fst == snd);
  • a != b // triess: a != b, !(a == b), and !(b == a)
  • <=>通过定义关系操作符来处理对象的顺序

1.2 使用例子

#include <fmt/core.h>

namespace less_cpp_20 {
class Value {
  private:
    int id;

  public:
    Value(int id) : id(id) {}

    [[nodiscard]] constexpr bool operator==(const Value &other) const noexcept {
        return id == other.id;
    }
    [[nodiscard]] constexpr bool operator!=(const Value &other) const noexcept {
        return !(*this == other);
    }
    bool operator<(const Value &other) const noexcept { return id < other.id; }
    bool operator>(const Value &other) const noexcept { return id > other.id; }
    bool operator<=(const Value &other) const noexcept {
        return id <= other.id;
    }
    bool operator>=(const Value &other) const noexcept {
        return id >= other.id;
    }
};
} // namespace less_cpp_20

namespace greater_cpp_20 {
class Value {
  private:
    int id;

  public:
    Value(int id) : id(id) {}
    [[nodiscard]] constexpr bool operator==(const Value &other) const noexcept {
        return id == other.id;
    }
    constexpr auto operator<=>(const Value &other) const noexcept {
        return id <=> other.id;
    }
};
} // namespace greater_cpp_20

void spaceship_operator() {
    less_cpp_20::Value v1{1}, v2{2};
    fmt::print("v1 {} v1\n", v1 == v1 ? "equal" : "is not equal");
    fmt::print("v1 {} v2\n", v1 < v2 ? "is less than" : "is not less than");
    greater_cpp_20::Value v3{3}, v4{4};
    fmt::print("v3 {} v3\n", v3 == v3 ? "equal" : "is not equal");
    fmt::print("v3 {} v4\n", v3 < v4 ? "is less than" : "is not less than");
}

int main() {
    //
    spaceship_operator();
    return 0;
}

2. <=>解释

<=>操作符不返回bool,它类似strcmp的返回值, 负值表示较小, 正值表示较大,0表示相等或者等效.

2.1 区分strong_orderingweak_orderingpartial_ordering

方面 strong_ordering weak_ordering partial_ordering
关系类型 严格全序 弱全序 偏序(部分可比较)
是否所有元素可比 可能不可比
相等的语义 真正相等 仅排序等价(不保证真相等) 可能不可比较
适用示例 整数、字符串、指针排序 按关键字段排序的复杂对象 浮点数(含NaN)

2.2 具体的例子

void ordering() {
    struct S {
      private:
        int x, y;

      public:
        S(int x, int y) : x(x), y(y) {}

        [[nodiscard]] constexpr std::weak_ordering
        operator<=>(const S &other) const {
            if (x < other.x)
                return std::weak_ordering::less;
            if (x > other.x)
                return std::weak_ordering::greater;
            return std::weak_ordering::equivalent;
        }

        [[nodiscard]] constexpr bool operator==(const S &other) const {
            return x == other.x;
        }
    };
    auto strong_ordering = 1 <=> 2;
    auto weak_ordering = S{1, 2} <=> S{2, 1};
    auto partial_ordering = 1.0 <=> 2.0;
    static_assert(
        std::is_same_v<decltype(strong_ordering), std::strong_ordering>);
    static_assert(std::is_same_v<decltype(weak_ordering), std::weak_ordering>);
    static_assert(
        std::is_same_v<decltype(partial_ordering), std::partial_ordering>);
}

3. 泛型代码的<=>

和正常的泛型代码定义6个比较运算符类似

template <typename T> struct S {
    T val{};

    [[nodiscard]] constexpr bool operator==(const S &other) const noexcept {
        return val == other.val;
    }
    constexpr auto operator<=>(const S &other) const noexcept {
        return val <=> other.val;
    }
};

// 为 S<T> 添加 fmt::formatter 支持
template <typename T> struct fmt::formatter<S<T>> {
    // 支持自定义格式,这里用默认格式解析即可
    constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); }

    // 格式化输出
    template <typename FormatContext>
    auto format(const S<T> &s, FormatContext &ctx) {
        return fmt::format_to(ctx.out(), "S{{val: {}}}", s.val);
    }
};

void template_ordering() {
    S<int> s1{1}, s2{2};
    fmt::print("s1 {} s1\n", s1 == s1 ? "equal" : "is not equal");
    fmt::print("s1 {} s2\n", s1 < s2 ? "is less than" : "is not less than");
    S<double> s3{3.0}, s4{4.0};
    fmt::print("s3 {} s4\n", s3 < s4 ? "is less than" : "is not less than");

    std::vector vec = {S<int>{3}, S<int>{2}, S<int>{1}};
    std::sort(vec.begin(), vec.end());
    for (auto &&item : vec) {
        fmt::print("{}\n", item);
    }
}

4. 完整代码实现

#include <fmt/core.h>

#include <algorithm>
#include <vector>

namespace less_cpp_20 {
class Value {
  private:
    int id;

  public:
    Value(int id) : id(id) {}

    [[nodiscard]] constexpr bool operator==(const Value &other) const noexcept {
        return id == other.id;
    }
    [[nodiscard]] constexpr bool operator!=(const Value &other) const noexcept {
        return !(*this == other);
    }
    bool operator<(const Value &other) const noexcept { return id < other.id; }
    bool operator>(const Value &other) const noexcept { return id > other.id; }
    bool operator<=(const Value &other) const noexcept {
        return id <= other.id;
    }
    bool operator>=(const Value &other) const noexcept {
        return id >= other.id;
    }
};
} // namespace less_cpp_20

namespace greater_cpp_20 {
class Value {
  private:
    int id;

  public:
    Value(int id) : id(id) {}
    [[nodiscard]] constexpr bool operator==(const Value &other) const noexcept {
        return id == other.id;
    }
    constexpr auto operator<=>(const Value &other) const noexcept {
        return id <=> other.id;
    }
};
} // namespace greater_cpp_20

void spaceship_operator() {
    less_cpp_20::Value v1{1}, v2{2};
    fmt::print("v1 {} v1\n", v1 == v1 ? "equal" : "is not equal");
    fmt::print("v1 {} v2\n", v1 < v2 ? "is less than" : "is not less than");
    greater_cpp_20::Value v3{3}, v4{4};
    fmt::print("v3 {} v3\n", v3 == v3 ? "equal" : "is not equal");
    fmt::print("v3 {} v4\n", v3 < v4 ? "is less than" : "is not less than");
}

void ordering() {
    struct S {
      private:
        int x, y;

      public:
        S(int x, int y) : x(x), y(y) {}

        [[nodiscard]] constexpr std::weak_ordering
        operator<=>(const S &other) const noexcept {
            if (x < other.x)
                return std::weak_ordering::less;
            if (x > other.x)
                return std::weak_ordering::greater;
            return std::weak_ordering::equivalent;
        }

        [[nodiscard]] constexpr bool operator==(const S &other) const noexcept {
            return x == other.x;
        }
    };
    auto strong_ordering = 1 <=> 2;
    auto weak_ordering = S{1, 2} <=> S{2, 1};
    auto partial_ordering = 1.0 <=> 2.0;
    static_assert(
        std::is_same_v<decltype(strong_ordering), std::strong_ordering>);
    static_assert(std::is_same_v<decltype(weak_ordering), std::weak_ordering>);
    static_assert(
        std::is_same_v<decltype(partial_ordering), std::partial_ordering>);
}

template <typename T> struct S {
    T val{};

    [[nodiscard]] constexpr bool operator==(const S &other) const noexcept {
        return val == other.val;
    }
    constexpr auto operator<=>(const S &other) const noexcept {
        return val <=> other.val;
    }
};

// 为 S<T> 添加 fmt::formatter 支持
template <typename T> struct fmt::formatter<S<T>> {
    // 支持自定义格式,这里用默认格式解析即可
    constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); }

    // 格式化输出
    template <typename FormatContext>
    auto format(const S<T> &s, FormatContext &ctx) {
        return fmt::format_to(ctx.out(), "S{{val: {}}}", s.val);
    }
};

void template_ordering() {
    S<int> s1{1}, s2{2};
    fmt::print("s1 {} s1\n", s1 == s1 ? "equal" : "is not equal");
    fmt::print("s1 {} s2\n", s1 < s2 ? "is less than" : "is not less than");
    S<double> s3{3.0}, s4{4.0};
    fmt::print("s3 {} s4\n", s3 < s4 ? "is less than" : "is not less than");

    std::vector vec = {S<int>{3}, S<int>{2}, S<int>{1}};
    std::sort(vec.begin(), vec.end());
    for (auto &&item : vec) {
        fmt::print("{}\n", item);
    }
}

int main() {
    //
    // spaceship_operator();
    // ordering();
    // template_ordering();
    return 0;
}