如何优化enum class的operator|=等位运算操作符实现?
问题描述
我使用多年前他人分享的enum class进行位运算,并新增了operator|=和operator&=操作符,其底层类型为uint8_t、uint16_t或uint32_t。目前我仅能通过两种方式实现该功能:
版本1(使用临时变量):
#define ENUM_FLAG_OPERATOR1(T,X) inline T & operator X (T & lhs, T rhs) { std::underlying_type_t <T> V; V = static_cast<std::underlying_type_t <T>>(lhs); V X static_cast<std::underlying_type_t <T>>(rhs); lhs = (T) V; return lhs; }
版本2(使用指针):
#define ENUM_FLAG_OPERATOR1(T,X) inline T & operator X (T & lhs, T rhs) { (*(reinterpret_cast<std::underlying_type_t <T> *>(& lhs))) X static_cast<std::underlying_type_t <T>>(rhs); return lhs; }
配套的宏定义如下:
#define ENUM_FLAG_OPERATOR2(T,X) inline T operator X (T lhs, T rhs) { return (T) (static_cast<std::underlying_type_t <T>>(lhs) X static_cast<std::underlying_type_t <T>>(rhs)); } #define ENUM_FLAGS(T, B) \ enum class T : B; \ inline T operator ~ (T t) { return (T) (~static_cast<std::underlying_type_t <T>>(t)); } \ ENUM_FLAG_OPERATOR2(T,|) \ ENUM_FLAG_OPERATOR2(T,^) \ ENUM_FLAG_OPERATOR2(T,&) \ ENUM_FLAG_OPERATOR1(T,|=) \ ENUM_FLAG_OPERATOR1(T,&=) \ enum class T : B
使用示例:
ENUM_FLAGS(AutostartFlags, uint8_t) { NO_FLAGS = 0, A = 0x01, B = 0x02, C = 0x10 }; AutostartFlags autostartDone_ = AutostartFlags::NO_FLAGS; ... autostartDone_ |= AutostartFlags::B;
请问是否存在更简洁、优雅的实现方式,无需临时变量或reinterpret_cast?我当前使用C14,运行环境为gcc 7.3.0,但也关注基于更高版本C的解决方案。
解决方案
C++14 兼容方案
可以通过一个模板辅助函数安全地获取enum class的底层类型引用,避免reinterpret_cast和临时变量的冗余:
辅助函数定义
#include <type_traits> template <typename Enum> constexpr auto& underlying_ref(Enum& e) noexcept { // 先转为void*再转底层类型指针,符合C++标准的类型转换规则 return *static_cast<std::underlying_type_t<Enum>*>(static_cast<void*>(&e)); }
运算符实现
替换原有宏定义,直接实现运算符模板(或针对特定枚举类型实现):
// 一元取反 template <typename Enum> inline Enum operator~(Enum t) noexcept { return static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(t)); } // 二元位运算 template <typename Enum> inline Enum operator|(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs)); } template <typename Enum> inline Enum operator^(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) ^ static_cast<std::underlying_type_t<Enum>>(rhs)); } template <typename Enum> inline Enum operator&(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs)); } // 复合赋值运算 template <typename Enum> inline Enum& operator|=(Enum& lhs, Enum rhs) noexcept { underlying_ref(lhs) |= static_cast<std::underlying_type_t<Enum>>(rhs); return lhs; } template <typename Enum> inline Enum& operator&=(Enum& lhs, Enum rhs) noexcept { underlying_ref(lhs) &= static_cast<std::underlying_type_t<Enum>>(rhs); return lhs; }
使用方式
无需再用ENUM_FLAGS宏,直接定义enum class即可:
enum class AutostartFlags : uint8_t { NO_FLAGS = 0, A = 0x01, B = 0x02, C = 0x10 }; // 正常使用位运算 AutostartFlags autostartDone_ = AutostartFlags::NO_FLAGS; autostartDone_ |= AutostartFlags::B;
如果需要限制仅针对特定枚举类型启用这些运算符,可以去掉模板,改为针对具体枚举类型实现,或者添加静态断言约束std::is_enum<Enum>::value和std::is_same<std::underlying_type_t<Enum>, uint8_t>::value等。
C++20+ 优雅方案
C++20引入了std::to_underlying和std::enum_class概念,能大幅简化实现,完全摆脱宏和不安全的转换:
#include <type_traits> // 约束仅针对enum class类型 template <std::enum_class Enum> constexpr Enum operator~(Enum e) noexcept { return static_cast<Enum>(~std::to_underlying(e)); } template <std::enum_class Enum> constexpr Enum operator|(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(std::to_underlying(lhs) | std::to_underlying(rhs)); } template <std::enum_class Enum> constexpr Enum operator^(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(std::to_underlying(lhs) ^ std::to_underlying(rhs)); } template <std::enum_class Enum> constexpr Enum operator&(Enum lhs, Enum rhs) noexcept { return static_cast<Enum>(std::to_underlying(lhs) & std::to_underlying(rhs)); } template <std::enum_class Enum> constexpr Enum& operator|=(Enum& lhs, Enum rhs) noexcept { lhs = lhs | rhs; return lhs; } template <std::enum_class Enum> constexpr Enum& operator&=(Enum& lhs, Enum rhs) noexcept { lhs = lhs & rhs; return lhs; }
优势
- 利用
std::to_underlying直接获取枚举的底层值,无需手动static_cast - 通过
std::enum_class概念确保运算符仅作用于强类型枚举 - 所有运算符都是
constexpr,支持编译期计算 - 完全无需宏定义,代码更简洁易读
内容的提问来源于stack exchange,提问作者Ernie Mur
相关产品推荐
相关产品推荐

