第一章 比较运算符<=>
¶
目录¶
0. 环境配置¶
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_ordering
和weak_ordering
和partial_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;
}