飞船运算符(<=>)与相等运算符(==)语义冲突问题问询
我定义了一个Point类,点的“大小”由其到原点的距离决定:若p1坐标平方和大于p2,则p1“更大”;两点距离相同时“大小”相同(如[1,2]和[-1,-2]),但仅当坐标完全一致时才“相等”(如[1,2]和[1,2])。
编写的类定义如下:
#include <iostream> class Point { private: int _x; int _y; public: Point(int x, int y): _x{x}, _y{y} {} bool operator==(const Point& p) const = default; auto operator<=>(const Point& p) const { return _x * _x + _y * _y <=> p._x * p._x + p._y * p._y; } };
但出现了矛盾:由于比较的是整数,<=>的返回类型被推导为std::strong_ordering,导致同距离不同坐标的点会被判定为std::strong_ordering::equal,与==的结果冲突:
int main () { Point p1{1,2}; Point p2{-1,-2}; std::cout << std::is_same_v<decltype(p1 <=> p2), std::strong_ordering> << std::endl; // true std::cout << (p1 <=> p2 == std::strong_ordering::equal) << std::endl; // true std::cout << (p1 == p2) << std::endl; // false return 0; }
我有几个疑惑:
- 是否有编译器工具能自动识别该场景不应使用强序?
- 作为
Point类设计者,是否必须保证<=>与==语义一致? - 我是否误解了
std::strong_ordering的定义?原以为p1<=>p2 == std::strong_ordering::equal当且仅当p1==p2,这个假设是否错误?
问题解答
1. 编译器工具能否自动识别该场景?
目前主流编译器(GCC、Clang、MSVC)没有内置功能自动检测<=>与==语义不一致的情况。不过可以通过静态分析工具(如Clang-Tidy)自定义规则来捕获这类问题,但默认规则库中没有专门针对此场景的检测项。
2. 类设计者是否必须保证<=>与==语义一致?
是的,C++标准明确要求:当且仅当a == b为真时,a <=> b必须返回对应序类型的相等值(比如std::strong_ordering::equal)。你的代码违反了这个要求,属于未定义行为——编译器不会报错,但使用标准库容器或算法时可能触发逻辑错误(例如std::set会把同距离不同坐标的Point视为同一元素)。
3. 对std::strong_ordering的理解是否正确?
你的假设完全正确:std::strong_ordering::equal的语义就是两个对象完全相等,对应==为真的情况。而你的场景需要的是“排序等价但不完全相等”,这正是std::weak_ordering::equivalent的语义。所以手动指定<=>返回std::weak_ordering是正确的修正方式:
#include <compare> // 需要包含此头文件 std::weak_ordering operator<=>(const Point& p) const { return _x * _x + _y * _y <=> p._x * p._x + p._y * p._y; }
内容的提问来源于stack exchange,提问作者antonio

