三路比较运算符 operator<=> 是 C++20 引入的真实运算符重载函数,需显式声明或定义;仅当需自定义比较逻辑(如字典序、解引用比较)或跳过成员/调整优先级时才必须手写,返回类型须严格匹配语义(如含浮点数时用 std::partial_ordering)。

三路比较运算符 operator 是什么,什么时候必须自己写
它不是语法糖,而是 C++20 引入的真正运算符重载函数,用于一次性定义所有比较关系(、<code>==、> 等)。编译器不会自动为你生成 operator,除非你显式声明为默认(= default)或手动实现。
只有当类需要自定义比较逻辑,且希望保持所有比较操作语义一致时,才需要自己写。比如成员含 std::vector 但你想按字典序比,或含指针但想按所指对象值比——这时候不能只靠 = default。
- 如果所有成员都支持三路比较,且你接受逐字段比较顺序,直接用
auto operator(const T&) const = default; - 若需跳过某个成员(如缓存字段),或调整比较优先级,就得手写
operator返回std::strong_ordering/std::weak_ordering/std::partial_ordering - 返回类型选错会编译失败:
std::strong_ordering要求完全可比较(无 NaN、无不可比状态),std::partial_ordering才支持NaN这类不等价也不大于/小于的情况
手写 operator 的典型错误:返回类型与实际逻辑不匹配
最常见的是该用 std::partial_ordering 却写了 std::strong_ordering,尤其在涉及浮点数或 std::optional 时。编译器不会帮你检查语义,只看类型是否可转换。
例如:
立即学习“C++免费学习笔记(深入)”;
struct Point {
double x, y;
auto operator<=>(const Point& p) const -> std::strong_ordering {
if (x != p.x) return x <=> p.x; // ❌ 若 x 或 p.x 是 NaN,这里行为未定义
return y <=> p.y;
}
};
正确做法是改用 std::partial_ordering,并接受 NaN anything 返回 std::partial_ordering::unordered。
- 只要类中任意成员使用了
std::partial_ordering(如float、double、std::optional<T>),整个operator就应返回std::partial_ordering -
std::strong_ordering和std::weak_ordering不支持 unordered 状态,强行比较 NaN 会导致未定义行为 - 别依赖编译器推导返回类型——显式写出来,避免模板实例化时意外推成错误类型
和传统比较运算符共存时,哪些会被自动合成
C++20 规定:只要你定义了 operator,编译器就自动合成 operator==(前提是没自己定义),但 不会 合成 operator!=、<、>= 等其他运算符——它们靠 ADL 查找 operator 并转换调用。
- 如果你同时写了
operator==和operator,前者会覆盖合成版本;但operator!=仍由!(a == b)推导,而非从来 - 要让
a 正确工作,不需要额外写 <code>operator<,只要确保operator可被找到(比如定义在类内或同命名空间) - 注意 ADL 限制:如果
operator在非成员、且不在参数类型的命名空间里,a 可能找不到它而编译失败
性能与兼容性:= default 真的够快吗
对纯数据类(POD),= default 生成的代码通常和手写 memcmp 一样高效,编译器会做结构体级比较优化。但它严格按声明顺序逐字段比,无法跳过 padding 或重排字段访问顺序。
- 如果成员中有大数组或慢比较的对象(如
std::string),而你其实只想先比 ID 字段就提前退出,= default就不合适,得手写短路逻辑 - 跨平台时注意:
= default比较依赖成员的布局和填充,若类有#pragma pack或不同编译器 ABI 差异,可能产生不一致结果 - 旧代码升级要注意:C++17 及以前没有
operator,所以用了它的代码无法在老标准下编译;头文件中若导出含operator的类,需加#if __cpp_impl_three_way_comparison防御
三路比较本身不难,难的是判断什么时候该用、用哪种 ordering、以及怎么让它和已有代码和平共处。最容易被忽略的是浮点字段的存在——它悄悄把整个比较逻辑拖进 partial_ordering 领域,而很多人还硬写着 strong_ordering。










