来自Bjarne Stroustrup's C++11 FAQ:
enum classes("new enums", "strong enums") 解决三个问题
使用传统的 C++ 枚举:
- 常规枚举隐式转换为 int,当有人不希望枚举充当整数时会导致错误。
- 传统枚举将其枚举数导出到周围范围,导致名称冲突。
- 无法指定
enum的底层类型,造成混淆,兼容性问题,并进行前向声明
不可能。
新的枚举是“枚举类”,因为它们将传统枚举(名称值)的各个方面与类的方面(作用域成员和没有转换)结合在一起。
因此,正如其他用户所提到的,“强枚举”将使代码更安全。
“经典”enum 的基础类型应该是一个足够大的整数类型,以适应enum 的所有值;这通常是int。此外,每个枚举类型都应与char 或有符号/无符号整数类型兼容。
这是对enum 基础类型必须是什么的广泛描述,因此每个编译器都会自行决定经典enum 的基础类型,有时结果可能会令人惊讶。
例如,我见过很多次这样的代码:
enum E_MY_FAVOURITE_FRUITS
{
E_APPLE = 0x01,
E_WATERMELON = 0x02,
E_COCONUT = 0x04,
E_STRAWBERRY = 0x08,
E_CHERRY = 0x10,
E_PINEAPPLE = 0x20,
E_BANANA = 0x40,
E_MANGO = 0x80,
E_MY_FAVOURITE_FRUITS_FORCE8 = 0xFF // 'Force' 8bits, how can you tell?
};
在上面的代码中,一些天真的编码人员认为编译器会将 E_MY_FAVOURITE_FRUITS 值存储为无符号 8 位类型...但对此没有任何保证:编译器可能会选择 unsigned char 或 int 或short,这些类型中的任何一个都足够大以适应enum 中看到的所有值。添加字段E_MY_FAVOURITE_FRUITS_FORCE8 是一种负担,不会强制编译器对enum 的底层类型做出任何选择。
如果有一些代码依赖于类型大小和/或假设 E_MY_FAVOURITE_FRUITS 具有一定宽度(例如:序列化例程),则此代码可能会以一些奇怪的方式运行,具体取决于编译器的想法。
更糟糕的是,如果某个同事不小心为我们的enum 添加了新值:
E_DEVIL_FRUIT = 0x100, // New fruit, with value greater than 8bits
编译器不会抱怨它!它只是调整类型的大小以适应enum 的所有值(假设编译器使用了可能的最小类型,这是我们做不到的假设)。对enum 的这种简单粗心的添加可能会微妙地破坏相关代码。
由于 C++11 可以为 enum 和 enum class 指定底层类型(感谢 rdb)所以这个问题得到了很好的解决:
enum class E_MY_FAVOURITE_FRUITS : unsigned char
{
E_APPLE = 0x01,
E_WATERMELON = 0x02,
E_COCONUT = 0x04,
E_STRAWBERRY = 0x08,
E_CHERRY = 0x10,
E_PINEAPPLE = 0x20,
E_BANANA = 0x40,
E_MANGO = 0x80,
E_DEVIL_FRUIT = 0x100, // Warning!: constant value truncated
};
如果字段的表达式超出此类型的范围,则指定基础类型,编译器将抱怨而不是更改基础类型。
我认为这是一个很好的安全改进。
那么为什么枚举类比普通枚举更受欢迎?,如果我们可以为作用域(enum class)和非作用域(enum)枚举选择底层类型,还有什么让enum class更好的选择?:
- 它们不会隐式转换为
int。
- 它们不会污染周围的命名空间。
- 它们可以被前向声明。