枚举类型的复合赋值运算符是否应基于算术运算符定义?
枚举类型运算符重载:打破常规是否值得?
常规类的运算符重载惯例
通常,在重载算术/位运算符时,我们都会遵循「基于复合赋值运算符定义普通运算符」的惯例。核心逻辑是复合赋值直接操作对象本身,避免不必要拷贝,效率更高;普通运算符则通过值参传入对象,复用复合赋值逻辑,兼顾代码复用性。示例如下:
class SomeClass { SomeType data; // ... public: SomeClass& operator+=(const SomeClass& other) { data += other.data; return *this; } friend SomeClass operator+(SomeClass l, const SomeClass& r) { return l += r; } friend SomeClass& operator|=(SomeClass& l, SomeClass r) { l.data |= r.data; return l; } friend SomeClass operator|(SomeClass l, SomeClass r) { return l |= r; } };
枚举类型的主流重载方案
但到了**枚举(包括未限定作用域枚举和enum class)**这里,社区主流方案却完全反过来:先定义普通算术/位运算符,再基于它们实现复合赋值运算符。比如:
using Underlying = uint8_t; enum class SomeEnum : Underlying { A_VALUE = 0b0000'0001, B_VALUE = 0b0000'0010, // ... H_VALUE = 0b1000'0000, CLEAR_ALL = 0, SET_ALL = static_cast<uint8_t>(-1), }; // 先定义普通位运算符 constexpr SomeEnum operator|(SomeEnum l, SomeEnum r) { return static_cast<SomeEnum>(static_cast<Underlying>(l) | static_cast<Underlying>(r)); } // 基于普通运算符实现复合赋值 constexpr SomeEnum& operator|=(SomeEnum& l, SomeEnum r) { return l = l | r; } // 反例:常规顺序实现(仅作对比) constexpr SomeEnum operator+=(SomeEnum& l, SomeEnum r) { Underlying ul = static_cast<Underlying>(l); ul += static_cast<Underlying>(r); return l = static_cast<SomeEnum>(ul); } constexpr SomeEnum operator+(SomeEnum l, SomeEnum r) { return l += r; }
据社区反馈,这种反向写法更受青睐——相关讨论的两个方案发布时间仅差半小时,但前者获赞量远高于后者。
打破常规的争议点
这种反向做法的优势很直观:
- 代码更简洁:普通运算符只需完成一次底层类型转换与运算,复合赋值直接复用逻辑,减少重复代码
- 编译期友好:更容易实现
constexpr支持,适配编译期计算场景
但它也带来了争议:
- 违反最小惊讶原则:绝大多数场景下,开发者默认
a += b比a + b更高效(前者直接操作对象,后者涉及临时对象拷贝),但枚举的这种实现中,operator+=依赖operator+,反而多了「运算+赋值」的步骤 - 打破「优先用复合赋值提升效率」的默认认知
于是我们面临一个实际问题:在定义枚举类型的运算符(通常指位运算符,也涵盖其他类型)时,是否应该遵循主流建议,打破常规的重载顺序?
务实的选择
其实在枚举场景下,所谓的「效率差异」几乎可以忽略不计:枚举的底层类型都是整数,拷贝成本极低,operator+的临时对象开销微乎其微。而代码简洁性、编译期友好性的收益,在大多数枚举的使用场景(比如位掩码操作)中,远大于这点可忽略的效率损失。
当然,如果你的枚举被用在极端性能敏感的循环中,确实可以回到常规顺序,优先实现operator+=来避免那一点点额外操作。但对于绝大多数日常开发场景,遵循社区主流的反向写法是更务实的选择——毕竟代码的可读性、可维护性,以及符合社区共识的惯例,往往比那点几乎测不出来的效率更重要。
内容的提问来源于stack exchange,提问作者Justin Time - Reinstate Monica
相关产品推荐
相关产品推荐

