根本不能重载的运算符有六个:.*、::、sizeof、typeid、?:、.;它们被c++标准语法禁止,编译器直接报错,不可绕过。

哪些运算符根本不能重载
直接说结论:.*、::、sizeof、typeid、?:(三目运算符)、.(成员访问)这六个,C++ 标准禁止你碰。不是“不推荐”,是语法上就不允许定义 operator.* 这种东西——编译器会直接报错 error: 'operator.*' must be a non-static member function,但你连写它的机会都没有。
常见误解是 sizeof 和 typeid 可以“绕过”,其实不行:它们是编译期运算符,和类型系统深度绑定,重载会破坏语义一致性。而 . 不能重载,是为了保证对象内存布局的可预测性;一旦放开,obj.member 到底调的是原生访问还是重载逻辑?编译器无法静态判定。
-
.*和->*是配对的,前者禁用,后者却可以重载(常用于智能指针模拟) -
?:禁用是因为其短路求值行为和三个操作数的控制流耦合太深,重载后无法保证左/右操作数是否被求值 - 所有作用域解析、类成员访问相关的运算符(
::、.、.*)都属于语言基础设施,不开放定制
能重载但必须是成员函数的运算符
有些运算符重载只能写成类的非静态成员函数,比如 operator=、operator[]、operator->、operator++(前置/后置)、operator--、operator()(函数调用)、operator new / operator delete 及其变体。
原因很实在:它们需要隐式访问 this 指针,且语义上天然绑定到当前对象。例如 a = b 的赋值,左边必须是可修改的左值对象,只有成员函数才能确保 this 是非 const 的(除非你故意写 const 成员函数,但那样连编译都过不去)。
立即学习“C++免费学习笔记(深入)”;
-
operator=如果写成全局函数,就无法区分a = b中的a是哪个对象——没有this,没法实现自赋值检查或资源管理 -
operator[]必须是成员,否则无法返回对内部数据的引用(比如vec[0] = 42要求返回int&) - 后置
operator++(int)的int形参只是标记,不是真传参;写成全局函数会导致签名冲突或语义错乱
能重载但建议用全局函数的运算符
像 operator+、operator、<code>operator==、operator 这些二元运算符,强烈建议写成非成员函数(通常配合 <code>friend 访问私有成员),尤其是当左操作数可能是隐式转换来的类型时。
典型坑:如果把 operator+ 写成成员函数,1 + obj 就编译不过——因为成员函数的左操作数固定是当前类类型,1 无法自动转成你的类再调用 operator+。而全局函数可以接受两个参数,都参与隐式转换。
operator 必须是全局函数,因为左操作数是 <code>std::ostream&,你不可能去改标准库的类加成员函数-
operator==写成全局更对称,避免a == b和b == a行为不一致(成员函数里b == a实际调的是b.operator==(a)) - 若需访问私有成员,加
friend声明即可,不破坏封装性
重载 operator bool 为什么容易翻车
很多人用 explicit operator bool() 来支持条件判断(如 if (obj) { ... }),但漏掉 explicit 就埋了大雷:它会让对象悄悄参与算术运算或指针比较,比如 obj + 1 或 obj == nullptr 都可能意外通过编译,结果不可控。
根本原因是,没有 explicit 的转换运算符会被编译器当成“隐式转换路径”,参与重载决议。而 explicit operator bool() 只在布尔上下文(if、while、!、&&、||)中触发,其他地方不生效。
- 别写
operator void*()这种老式惯用法,它比bool更危险:能隐式转成任意指针类型,int* p = obj;居然能编译 - 即使加了
explicit,也要注意返回值:必须返回bool字面量或能转成bool的表达式,别返回int或指针(虽然能转,但可读性差、易误用) - 某些模板代码(比如
std::vector::erase的返回值判断)依赖explicit operator bool(),没它可能触发 SFINAE 失败
事情说清了就结束。最常被忽略的是:禁用列表里的运算符,不是“还没实现”,而是语言设计上就拒绝抽象;而 explicit operator bool() 那个 explicit,少打一个字母,调试三天都不见得能定位到。










