暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Item10 优先使用枚举类而不是无作用域枚举

程序员的Cookbook 2020-07-30
801

   通常来说我们在花括号中定义的名称其作用域就在花括号中,但是C++98的枚举类型的声明却不遵从这个规则。

   事实上,上面这些枚举名称都暴露到外层的作用域中了,官方称这种枚举类型成为unscoped,也就是无作用域限制的,在C++11中引入了scoped的枚举类型,枚举的名称不会暴露到外层作用域中。

   通过使用scoped的枚举类型,可以减少因为作用域的问题带来的名称污染,除了作用域外,scoped的枚举类型还有另外一个优点就是强类型,不会进行隐式类型转换,而unscoped的枚举类型可以隐式转换为整型。

   如果非要进行转换的话,可以借助 static_cast
来进行显示的转换,除了上面提到的几个优点外,scoped类型还可以前向声明,这样可以加快编译的速度,看到这里的读者可能会问unscoped的枚举类型难道不能前向声明吗?

   通过实验,果不其然unscoped的枚举类型不支持前向声明!,好吧我承认我在这里说了一个慌,它是支持前向声明的,只不过不是这样前向声明的而已,接下来让我们解析下。前向声明,其实就是提前声明,而声明就是告诉目标是什么类型,长什么样子。具体的内容等用到了再去看它的定义。然而有一个事实大多数人却不知道,unscoped枚举类型的实际类型并不是enum,它有一个底层存储类型。而这个底层存储类型是编译器在编译的时候决策的,根据你的取值范围来定义你的底层存储类型。

   上面的Color的实际存储类型可能就是 char
类型,足够存储了。这一切的目的就是为了节省空间。也就是unscoped的实际类型是不定,是由编译器负责选择,所以你前向声明的时候也就没办法指明其类型了。所以不支持上面这种方式来前向声明,但是可以在指定底层存储类型的情况下进行前向声明。有的读者可能好奇为什么C++11中引入的scoped枚举类型可以前向声明,还有如何指定底层存储类型。对于第一个问题,答案很简单,那就是scoped的底层存储类型是默认的,默认是int,我们也可以指定其它的存储类型。

那么 unscoped
枚举类型如何指定底层存储类型呢?

   上面对比了两种不同的枚举类型,scoped优势明显,但是unscoped也有自己的优点,它的优点我认为最为重要的一点就是把一些无意义的数值有意义化,典型的比如函数返回值,下标位置等。

   上面使用了C++11中的tuple来表示一个用户的姓名,email,和年龄等信息,通过 std::get<1>
取出email信息,很明显数值1在这里是无意义的不易读,如果换成枚举类型就会很易读了。

但是如果上面的代码换成使用scoped枚举类型就会显得臃肿。

   需要先转换为 std::size_t
类型,这里有点投机取巧,不应该转换为 std::size_t
类型的,应该转换为枚举类型的底层存储类型,因为如果底层存储类型比较大,转换成 std::size_t
可能会导致窄化。为此需要有个手段获取枚举类型的底层存储类型。而且还需要是编译时获取,因为获取的值是作为 std::get
这个模板的参数。

Tips

  1. C++98种的枚举众所周知是无作用域限制的

  2. C++11中的枚举类是有作用域限制的,不能进行隐式的类型转换需要使用C++的类型cast进行转换

  3. 无论是枚举类还是传统的枚举类型都支持指定底层的存储,对于枚举类来说默认的底层存储类型是int,而传统的枚举类型其底层存储是未知的,需要在编译器进行选择

  4. 枚举类总是可以进行前向声明的,而枚举类型则不行,必须是在明确指定其底层存储的时候才能进行前向声明



文章转载自程序员的Cookbook,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论