operator==必须是const成员函数或非成员函数;推荐非成员形式以保证对称性和隐式转换支持;比较需覆盖所有语义相关成员,避免自赋值、基类遗漏、指针浅比较、浮点数直接比较等错误。

operator== 必须是 const 成员函数或非成员函数
重载 operator== 时最常踩的坑:把它写成普通成员函数却没加 const。比如:bool operator==(const MyClass& other) { ... } —— 这会导致 a == b 在 a 是 const 对象时编译失败,因为隐式 this 指针无法绑定到非 const 成员函数。
正确写法只有两种:
- 作为
const成员函数:bool operator==(const MyClass& other) const { ... } - 作为非成员(通常声明为
friend):friend bool operator==(const MyClass& a, const MyClass& b) { ... }
推荐用非成员形式,它天然对称(a == b 和 b == a 行为一致),也方便支持隐式类型转换(比如 my_obj == 42 需要 int 转 MyClass 时,只有非成员能触发右侧转换)。
比较逻辑必须覆盖所有相关成员,且避免自赋值陷阱
很多人只比较几个字段就以为够了,结果出现“两个对象内容一样但 == 返回 false”。核心原则:只要会影响对象语义相等性的数据成员,就必须参与比较;无关的(比如缓存、引用计数、mutex)不能参与。
立即学习“C++免费学习笔记(深入)”;
常见错误场景:
- 忘记比较基类子对象:
return Base::operator==(other) && this->member == other.member; - 指针成员直接比地址:
ptr == other.ptr→ 应该比指向内容是否等价(深比较),除非业务明确要求“同一块内存才算相等” - 容器成员用
==比较即可(std::vector、std::string等已重载),别手写循环 - 浮点数慎用
==,应改用近似比较(如std::abs(a - b) ),否则 <code>0.1 + 0.2 != 0.3会破坏相等性传递性
与 std::hash 和 std::equal_to 的兼容性容易被忽略
如果你把类用在 std::unordered_map 或 std::unordered_set 里,只重载 operator== 不够——还必须提供匹配的 std::hash 特化,且满足:如果 a == b 为 true,则 std::hash<myclass>{}(a) == std::hash<myclass>{}(b)</myclass></myclass> 必须为 true。
另一个坑:STL 算法如 std::find 默认用 operator==,但 std::equal_to 函数对象也依赖它。如果重载了 operator== 却没让它参与 ADL(比如没放在类所在命名空间),某些模板实例化可能找不到正确的重载,导致静默调用内置 ==(逐字节比较)。
确保做法:
-
operator==声明和定义都在类定义内,或同名命名空间下 - 若用非成员形式,不要放在匿名命名空间或内联命名空间里
- 测试时用
std::unordered_set<myclass></myclass>插入两个相等对象,看是否去重成功
移动语义下 operator== 仍需正常工作
对象被移动后,其状态是“有效但未指定”(valid but unspecified),但 operator== 仍必须可安全调用、不崩溃、符合逻辑。比如你写了 std::move(obj),之后再写 obj == obj,应该返回 true(自反性),哪怕 obj 内部指针已置空。
这意味着:
- 别在
operator==里做可能导致未定义行为的操作(如解空指针、访问 moved-from 容器的data()) - 如果类有 move 构造/赋值,检查 moved-from 对象的各成员是否处于可比较状态(例如
std::vector移动后是空但合法,==没问题;裸指针移动后若置为nullptr,比较时需先判空) - 不要依赖“移动后对象等于默认构造对象”这种假设——标准不保证,不同 STL 实现有差异
真正麻烦的是自定义资源管理类,这时候得在 moved-from 状态设计一个明确的、可比较的“空值语义”,然后让 operator== 尊重它。








