C++比较对象重载解析问题:飞船运算符为何被模板运算符选中?
为什么类中定义<=>后,全局模板operator>仍被优先选择?
核心原因分析
1. <=>未触发默认比较运算符生成
C++20规定,只有满足以下条件时,编译器才会为类自动生成所有比较运算符(包括operator>):
operator<=>的返回类型是std::strong_ordering、std::weak_ordering或std::partial_ordering(或可隐式转换为这些类型的标准类型);operator<=>使用= default声明,或编译器能自动推导其默认实现。
如果你的operator<=>是自定义实现且返回非标准类型(比如int),或者仅声明未定义/未使用= default,编译器不会生成对应的operator>。此时调用a > b时,只能匹配全局的模板运算符。
反例(不会生成默认operator>):
class MyClass { public: // 返回非标准类型,编译器不生成默认比较运算符 int operator<=>(const MyClass& other) const { return this->x - other.x; } private: int x; };
2. 重载决议中模板的意外可行匹配
即使operator<=>生成了operator>,但如果该operator>是成员函数,而全局模板是非成员模板,在某些类型转换场景下,成员operator>可能不可行,而模板通过实参推导成为可行函数:
- 比如用派生类对象与基类对象比较时,基类的成员
operator>无法直接接受派生类参数(无隐式转换),但全局模板可以推导匹配Derived和Base类型,从而被选中; - 再比如非const对象与const对象比较时,若成员
operator>的参数要求严格匹配,而模板的const L&/const R&能兼容转换,也可能优先匹配模板。
3. 跨类型比较无对应非模板运算符
operator<=>默认生成的比较运算符仅支持同类型比较。如果你实际调用的是跨类型比较(如MyClass与OtherClass),此时没有对应的非模板operator>,编译器只能匹配全局模板,通过r < l调用已有的operator<实现。
解决方案
1. 确保<=>符合标准生成规则
使用= default让编译器自动生成标准的比较运算符,同时包含<compare>头文件:
#include <compare> class MyClass { public: // 自动生成所有比较运算符(包括operator>) auto operator<=>(const MyClass&) const = default; private: int x; };
2. 限制全局模板的适用范围
通过SFINAE让全局模板仅在类没有原生<=>运算符时生效,避免干扰标准比较运算符:
#include <compare> #include <type_traits> // 仅当L和R之间无原生<=>运算符时,才启用该模板 template <typename L, typename R> requires (!requires(const L& l, const R& r) { l <=> r; }) bool operator>(const L& l, const R& r) { return r < l; }
3. 显式定义跨类型比较运算符
若需要跨类型比较,直接为对应类型对定义非模板operator>,优先级高于模板:
class MyClass; class OtherClass; bool operator>(const MyClass& l, const OtherClass& r) { // 自定义跨类型比较逻辑 return r < l; }
内容的提问来源于stack exchange,提问作者T.D.
相关产品推荐
相关产品推荐

