为什么比起无作用域枚举,更偏爱有作用域枚举呢?
摘要:本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢! 博客已经迁移到 "这里啦" 一般情况下,
本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!
博客已经迁移到这里啦
一般情况下,在花括号中声明一个name(包括变量名,函数名),这个name的可见性会被限制在花括号的作用域内。对于在C++98风格的enum中声明的enum成员却不是这样。这些enum成员的name属于的作用域是enum所在作用域,这意味着在这个作用域中,不能拥有相同的name:
enum Color { black, white, red }; //black,white,red
//和Color在同一个作用域
auto white = false; //错误!white在这个
//作用域已经声明过了
所以事实上,这些enum成员name泄露到enum所在的作用域中去了,这导致官方对于这种enum给出了一个官方术语:unscoped。新的C++11中有一个与此相对应的版本:scoped enum,不会像这样让name泄露:
enum class Color { black, white, red }; //black,white red
//在Color作用域中
auto white = false; //好的,没其他white
Color c = white; //错误!在这个作用域中没有
//一个叫“white”的enum成员
Color c = Color::white; //对的
auto c = Color::white; //也是对的(而且和Item 5的建议一样)
因为scoped enum通过“enum class”声明,它们有时候也被叫做enum类。
单是减少命名空间的污染就足够作为理由让我们更偏爱scoped enum了,但是scoped enum还有第二个压倒性的优点:它们的成员属于强类型。unscoped enum的成员能隐式转换到数值类型(然后,从数值类型,可以转换到浮点类型)。因此像下面这样,在语义上是扭曲的代码是完全有效的:
enum Color { black, white, red}; //unscoped enum
std::vector<std::size_t> //函数,返回x的素因数
primeFactors(std::size_t x);
Color c = red;
...
if(c < 14.5) { //把Color和double数比较(!)
auto factors = //计算Color的素因数(!)
primeFactors(c);
...
}
然而,在”enum“后面添加一个简单的”class“,就能把unscoped enum转变为scoped enum,并且情况会发生很大的改变。这里没有从enum的成员到任何其它类型的隐式转换:
enum class Color { black, white, red }; //scoped
Color c = Color::red;
...
if (c < 14.5) { //错误,不能把Color和double
//数进行比较
auto factors = //错误,函数需要一个std::size_t
primeFactors(c); //不能传一个Color进去
...
}
如果你真的想要把Color转换到不同的类型,你需要做的就是:使用cast把Color转换成你需要的类型:
if(static_cast<double>(c) < 14.5) { //奇怪的代码,但是有效
auto factors = //可疑的,但是能通过编译
primeFactors(static_cast<std::size_t>(c));
...
}
比起unscoped enum,scoped enum看起来还有第三个优点,因为scoped enum可以前置声明。也就是,他们的name可以在声明的时候不定义(不明确它们的成员):
enum Color; //错误
enum class Color; //对的
这是一个误导。在C++11中,unscoped enum也能前置声明,但是需要做一些额外的工作。这个工作源于一个事实,就是C++中的每个enum都有一个整形的基础类型,这个类型由编译器决定。
