C++中定义安全高性能类枚举:std::variant与enum性能对比
问题背景
我们需要定义一个仅包含red、green、blue三个值的Color类型,要求能区分不同值(如red==red、red!=blue均为true)。在Haskell中可以一行实现:
data Color = Red | Green | Blue deriving Eq -- ctors have a capital first letter
在C++中,为了规避基础enum的一些问题,有人用std::variant实现了如下方案:
namespace colors { namespace detail { inline constexpr struct Red{} red; inline constexpr struct Green{} green; inline constexpr struct Blue{} blue; } using detail::red; using detail::green; using detail::blue; using Color = std::variant<detail::Red, detail::Green, detail::Blue>; inline constexpr bool operator==(Color const& first, Color const& second) { return first.index() == second.index(); } inline constexpr bool operator!=(Color const& first, Color const& second) { return !(first == second); } }
客户端使用示例:
#include <cassert> #include "the-file-above.hpp" int main() { using namespace colors; Color color; assert(color == red); color = green; assert(color == green); color = blue; assert(color == blue); color = red; assert(color == red); }
现在提问:从性能角度,是否存在客观理由更偏好基础enum方案?
性能角度的客观理由:优先选择基础enum
从性能层面来看,确实存在明确的客观理由优先选择基础enum方案,具体体现在以下几点:
1. 内存占用显著更小
基础enum的底层类型默认是int(通常4字节),也可以显式指定更小的类型比如uint8_t(仅1字节)。而std::variant的内存占用是其所有备选类型中最大的尺寸,加上额外存储的类型索引(通常1或4字节)。这里的三个空结构体尺寸均为1字节,因此Color variant的大小至少是2字节(1字节结构体+1字节索引),比最小化的enum(1字节)多出一倍,比默认enum也有额外开销。
2. 比较操作更高效
enum的相等/不等比较是直接的整数比较,对应单条CPU指令,几乎没有开销。而即使是你优化后的variant比较(通过index()判断),也需要先读取variant内部存储的索引值再做比较——虽然编译器可能会做优化,但理论上比enum的直接整数比较多了一步内存访问操作。如果使用variant默认的operator==(而非自定义的基于index的实现),开销会更大,因为需要逐个类型匹配后再比较。
3. 构造与赋值操作更快
enum的构造和赋值就是简单的整数赋值,仅需单条CPU指令。而variant的构造/赋值不仅要构造备选类型的实例(这里是空结构体,无额外开销),还需要写入对应的类型索引值,比enum多了一步内存写入操作。
4. 缓存友好性更优
更小的内存占用意味着在数组、容器中存储大量Color实例时,能将更多元素装入CPU缓存,减少缓存缺失(cache miss)的概率,在批量处理场景下能显著提升整体性能。
需要说明的是,std::variant方案确实解决了基础enum的一些痛点(比如避免隐式转换为整数、提供更强的类型安全性),但这些优势是以牺牲部分性能为代价的。如果你的场景对性能要求极高(比如高频处理大量Color数据),且能接受enum的一些限制,那么基础enum是更优的选择。
内容的提问来源于stack exchange,提问作者Enlico

