本周小贴士#86:带类的枚举

Posted -飞鹤-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了本周小贴士#86:带类的枚举相关的知识,希望对你有一定的参考价值。

作为totw/86最初发表于2015年1月5日

由Bradley White (bww@google.com)创作

“显示类,…并显示字符。”——贝尔.布莱恩特

enumeration,简称enum,是一种可以容纳一组指定整数中的一个类型。此集合中的某些值可以命名,它们被称为枚举值。

无作用域枚举

C++程序员很熟悉这个概念,但在C++11之前,枚举有两个重大的缺陷:枚举的名称:

  • 在与枚举类型相同的作用域内,然后隐式转换为某种整形值。
    因此,使用C++98…
enum CursorDirection { kLeft, kRight, kUp, kDown };
CursorDirection d = kLeft; // 可行:作用域内的枚举器
int i = kRight;            // 可行:枚举器转换为整形

然而,…

// 错误:重新声明kLeft和kRight
enum PoliticalOrientation { kLeft, kCenter, kRight };

C++11以一种方式修改无作用域枚举的行为:枚举值对于枚举而言是局部的,但为了向后兼容,它会继续导出到枚举的作用域中。
因此,使用C++11…

CursorDirection d = CursorDirection::kLeft;  // 在C++11中可行
int i = CursorDirection::kRight;             // 可行:继续转换为整形

但PoliticalOrientation的声明依然将引起错误。

作用域枚举

已经观察到的隐式转换为整形是错误的来源,而枚举值与枚举在相同作用域造成的命名空间污染,会在大型且多库项目中引起问题。为了解决这些担忧,C++11引入了一个新的概念:作用域枚举。

在一个由关键字enum class引入的作用域枚举中,这些枚举值是:

  • 仅作为枚举(它们它们不会导出到枚举的作用域中)的局部变量,并且不会被隐式转换为整形。

因此,(注意额外的class关键字)…

enum class CursorDirection { kLeft, kRight, kUp, kDown };
CursorDirection d = kLeft;                    // 错误,kLeft不在这个作用域内
CursorDirection d2 = CursorDirection::kLeft;  // 可行
int i = CursorDirection::kRight;              // 错误:无法转换

和…

// 可行:kLeft和kRigh都是每个作用域枚举的局部变量
enum class PoliticalOrientation { kLeft, kCenter, kRight };

这些简单的更改消除了普通枚举的问题,因此在所有新代码中应该优先使用enum class。

使用作用域枚举确实意味着,如果你仍然想如此的转换(例如,当记录一个枚举值时,或在类似标志的枚举值上使用按位运算时),你必须显式转换为整形。使用std::has进行散列将继续工作(例如,std::unorderd_map<CursorDirection, int>)。

底层枚举类型

C++11还为两类枚举类型引入了指定底层类型的能力。以前枚举的底层整型类形是通过枚举值的符号和大小来确定的,但是我们现在能够明确指定了。例如,…

// 使用'int'作为CursorDirection的底层类型
enum class CursorDirection : int { kLeft, kRight, kUp, kDown };

因为这个枚举值的范围很小,并且如果我们希望在存储CursorDirection时避免浪费空间,那么我们可以指定char来代替。

// 使用'char'作为CursorDirection的底层类型
enum class CursorDirection : char { kLeft, kRight, kUp, kDown };

如果一个枚举值超过了底层类型的范围,那么编译器会报错。

结论

在新代码中优先使用enum class。你将减少命名空间污染,并且将避免在隐式转换中的错误。

enum class Parting { kSoLong, kFarewell, kAufWiedersehen, kAdieu };

以上是关于本周小贴士#86:带类的枚举的主要内容,如果未能解决你的问题,请参考以下文章

本周小贴士#147:负责地使用穷举witch语句

本周小贴士#101:返回值,引用和生命周期

本周小贴士#120:返回值是不可触碰的

本周小贴士#99:非成员接口规范

本周小贴士#108:避免std::bind

本周小贴士#122: 测试固定装置,清晰度和数据流