如果在一个大括号里面声明一个名字,则该名字的可见性就被限定在括号扩起来的作用域内。但是这个规则不适用于C++98
风格的枚举类型中定义的枚举量;这些枚举量的名字属于包含着这个枚举类型的作用域,意味这在此作用域内不能有其他实体取相同的名字;
enum Color {black, white, red}; // black,white,red所在作用域和Color相同
auto white = false; // 错误!white已经在范围内被申明过
这些枚举常量的名字泄漏到枚举类型所在作用域的这一事实,对应一个术语:“不限定范围的枚举类型”;它们在C++11
中的对等物:”限定作用域的枚举类型“;
enum class Color {black, white, red}; // black,white,red所在作用域被限定在Color内
auto white = false; // 没问题,范围内并无其他”white“
Color c = white; // 错误!范围内无名为”white“的枚举量
Color c = Color::white; // 无问题
auto c= Color::white; // 无问题
限定作用域的枚举类型带来:(1)名字空间污染降低;(2)枚举量是更强类型的;不限范围的枚举类型中枚举量可以隐式转换成整数类型,这么一来,如下的语义怪胎却成为完全合法的了;
enum Color {black, white, red}; // 不限定范围的枚举类型
std::vector<std::size_t> primeFactors(std::size_t x);
Color c = red;
if (c < 14.5) { // 将Color类型与double类型进行比较
auto factors = primeFactors(c);
}
仅仅是把一个简简单单的class
加到enum
之后,语义就变得完全不同了,从限定作用域的枚举类型到任何其他类型都不存在隐式转换路进:
enum class Color {black, white, red}; // 限定范围的枚举类型
std::vector<std::size_t> primeFactors(std::size_t x){
return std::vector<std::size_t>();
}
Color c = Color::red;
if (c < 14.5) { // 错误!不能将Color类型和double类型数值比较
auto factors = primeFactors(c); // 错误!不能将Color类型传入要求std::size_t类型的参数
}
限定作用域相比不限定作用域的第三个优点就是可以进行前置声明,即其类型名字可以比其中枚举量先声明:
enum Color; // 错误
enum class Color; // 没问题
一切枚举类型在C++
里都会由编译器来选择一个整数类型作为其底层类型;如:
enum Color {black, white, red};
编译器会选择char
作为其底层类型,因为只有三个值表示;但是有些枚举类型取值范围就大得多,如:
enum Status {
good = 0,
failded = 1,
incomplete = 100,
corrupt = 200,
indeterminate = 0XFFFFFFFF
}
这里,需要表示的取值范围是从0到0xFFFFFFFF为了节省内存,编译器通常会为枚举类型选用足够表示枚举量取值的最小底层类型;
前置声明能力的缺失还是会造成一些弊端。其中之一就是它会增加编译依赖性:
enum Status {
// ...
audited = 500,
//...
}
这里增加了一个枚举量audited
,可能整个系统都会因此需要重新编译,即使只有一个函数用到了这个新的枚举量audited
;而这种事情利用C++11
中为枚举类型提供的前置声明能力即可破除;举例:
enum class Status; // 前置声明
void continueProcessing(Status s);// 采用前置声明的枚举类型
若头文件中包含了这些声明,则在Status
定义发生了修订时,就不会要求重新编译,即使Status
被修改了(如增加了一个audited
枚举量),但是contineProcessing
的行为未受影响(例如,由于continueProcessing
并未使用audited
),则coninueProcessing
的实现也同样无须重新编译;
可是如果编译器需要在枚举类型被使用前就知晓其尺寸,为什么C++11
中的枚举类型就可以就行前置声明,C++98
中就不行呢?因为:限定作用域枚举类型的底层类型是已知的;而对于不限定范围的枚举类型,你也可以指定这个底层类型;
enum class Status; // 底层类型是int
enum class Status:std::uint32_t; // Status的底层类型为std::uint32_t
如果要指定不限定范围的枚举类型的底层类型,做法和限定作用域的枚举类型一样;这样做了以后,不限范围的枚举类型也能进行前置类型声明了:
enum Color:std::uint8_t; // 不限定范围的枚举类型的前置声明,底层类型为std::uint8_t
底层类型指定同样也可以在枚举类型定义中进行:
enum class Status : std::uint32_t{...}