枚举可以将一组整型常量组织在一起,和类一样,每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。
C++ 包含两种枚举,限定作用域的和不限定作用域,其中 C++ 11 新标准引入了限定作用域的枚举类型。
限定作用域的枚举类型定义:
enum class Color {red,yellow,green}; //@ 使用 class 关键字
enum struct Color {red,yellow,green}; //@ 使用 struct 关键字
不限定作用域的枚举类型定义,省去 class 或者 struct 关键字,枚举类型的名字是可选的:
enum Color {red,yellow,green}; //@ 命名的不限定作用域的枚举类型
enum {red,yellow,green}; //@ 未命名的不限定作用域的枚举类型
如果 enum 是未命名的,则只能在定义该 enum 时定义它的对象。
枚举成员
- 限定作用域的枚举类型中,枚举成员的名字遵循常规的作用域准则,并且在枚举类型的作用域外是不可访问的。
- 在不限定作用域的枚举类型中,枚举成员的作用域于枚举类型本身的作用域相同。
enum color{ red, yellow, green }; //@ 不限定作用域的枚举类型
enum stoplight{ red, yellow, green }; //@ 错误,重复定义了枚举成员
enum class peppers{ red, yellow, green }; //@ 正确,枚举成员被隐藏了
color eyes = green; //@ 正确,不限定作用域的枚举类型的枚举成员位于有效的作用域中
peppers p = green; //@ 错误,peppers 的枚举成员不在有效的作用域中
//@ color::green 在有效作用域中,但是类型错误
color hair = color::green; //@ 正确,允许显示地访问枚举成员
peppers p2 = peppers::green; //@ 正确,使用peppers::green
默认情况下,枚举值从0开始,依次加1,但是也可以指定枚举成员的值,并且枚举值不一定唯一:
enum class intTypes {
charTyp = 8, shortTyp = 16, intTyp = 16,
longTyp =32, long_longTyp = 64
};
- 枚举成员是 const,因此初始化枚举成员时提供的初始值必须是常量表达式,也可以在任何需要常量表达式的地方使用枚举成员。例如:
constexpr intTypes charbits = intTypes::charTyp;
- 可以将 enum 作为语句的表达式,而将枚举值作为 case 标签。
- 可以将 enum 作为一个非类型模板形参使用。
- 可以在类的定义中初始化枚举类型的静态数据成员。
枚举也可以定义新的类型
只要 enum 有名字,就能定义并初始化该类型的成员,要想初始化 enum 对象或者为 enum 对象赋值,必须使用该类型的一个枚举成员或者该类型的另一个对象:
enum class open_modes {input,output,inoutput};
open_modes om = 2; //@ 错误,2 不属于 open_modes 类型
open_modes om = open_modes::input; //@ 正确
一个不限定作用域的枚举类型的对象或枚举成员自动转换成整型,因此可以在任何需要使用整型值的地方使用它们:
int i = color::red;
int j = peppers::red;
但是对于限定作用域的枚举不能赋值给整型值:
int i = open_modes::input; //@ 错误
指定 enum 的大小
尽管每个 enum 都定义了唯一的类型,但是实际上 enum 是由某种整数类型表示的,在 C++11标准中,可以在 enum 的名字后面加上冒号鄙视想在该 enum 中使用的类型:
enum intValues : unsigned long long {
charType = 255, shortType = 65535, intType = 65535,
longType = 4294967295UL,
long_longType = 18446744073709551615ULL
};
如果没有指定 enum 的潜在类型:默认情况下限定作用域的 enum 成员的类型是 int。不限定作用域的枚举类型,枚举成员不存在默认类型,只知道成员的潜在类型足够大能够容纳枚举值。如果指定了枚举成员的类型,包括对限定作用域的 enum 的隐式指定,则一旦某个枚举成员的值超出了该类型所能容纳的范围,将引发程序错误。
枚举类型的前置声明
在 C++ 11 新标准中,可以提前声明 enum。enum的前置声明无论隐式的还是显式的必须指定其成员大小。
- 不限定作用域的额 enum 未指定成员的默认大小,因此每个声明必须指定成员大小。
- 限定作用域的 enum,可以不指定其成员大小,这个值被隐式地定义成 int。
//@ 不限定作用域的枚举类型 intValues 的前置声明必须指明成员类型
enum intValues : unsigned long long;
//@ 限定作用域的枚举类型可以使用默认成员类型 int
enum class open_modes;
enum 的声明和定义必须匹配,这意味着该 enum 的所有声明和定义中成员的大小必须一致。
不能在上下文中先声明一个不限定作用域的 enum 名字,然后再声明一个同名的限定作用域的 enum。
enum class intValues;
enum intValues; //@ error,intValues 已经被声明成限定作用域的 enum
enum intValues : long; //@ error,intValues 已经被声明成 int
形参匹配与枚举类型
要初始化一个 enum 对象,必须使用该 enum 类型的另一个对象或者它的一个枚举成员。因此,即使某个整型值恰好与枚举成员的值相等,他也不能作为函数的 enum 实参使用:
enum Tokens{INLINE = 128,VIRTUAL=129};
void ff(Tokens);
void ff(int);
int main()
{
Tokens curToken = INLINE;
ff(128); //@ 精确匹配 ff(int)
ff(INLINE); //@ 精确匹配 ff(Tokens)
ff(curToken); //@ 精确匹配 ff(Tokens)
return 0;
}
尽管不能直接将整型值直接传递给 enum 形参,但是可以将一个不限定作用域的枚举类型的对象或枚举成员传给整型形参。此时,enum 的值提升为 int 或者更大的整型,实际提升的结果由枚举类型的潜在类型决定:
void newf(unsigned char);
void newf(int);
unsigned char uc = VIRTUAL;
newf(VIRTUAL); // @ 调用 newf(int)
newf(uc); // @ 调用 newf(unsigned char)
Tokens 可以使用 unsigned char 来表示,因此很多编译器会使用 unsigned char 作为 Tokens 的潜在类型。
不管 Tokens 的潜在类型是什么,它的对象和枚举成员都提升成 int,即使枚举值可以用 unsigned char 存储也是如此。