枚举声明
枚举是独立类型,其值限制在值的一个范围内(见后述细节),它可以包含数个显式指名的常量(“枚举项”)。常量的值是整数类型的值,该类型被称作枚举的底层类型。
枚举由 enum-specifier 定义,它出现于声明语法的 decl-specifier-seq 。 enum-specifier 拥有下列语法:
enum-key attr(可选) identifier(可选) enum-base(可选) { enumerator-list(可选) }
|
(1) | ||||||||
enum-key attr(可选) nested-name-specifier(可选) identifier enum-base(可选) ;
|
(2) | (C++11 起) | |||||||
enum-key | - | enum 、 enum class (C++11 起) 或 enum struct (C++11 起) 之一
|
attr(C++11) | - | 任意数量属性的可选序列 |
identifier | - | 被声明的枚举的名称。若存在,且若此声明为重声明,则它可以前附 nested-name-specifier(C++11 起) :名称和作用域解析运算符 :: 的序列,以作用域解析运算符结尾。名称仅可于无作用域枚举声明省略
|
enum-base(C++11) | - | 冒号 (: ) ,后随指名整数类型的 type-specifier-seq (若它为 cv 限定,则忽略限定),该类型将作为此枚举类型的固定底层类型
|
enumerator-list | - | 枚举项定义的逗号分隔列表,每项要么是简单的 identifier ,它成为枚举项之名,要么是带初始化器的标识符: identifier = constexpr 。任一情况下, identifier 可直接后随一个可选的书型指定符序列。 (C++17 起)
|
有二种截然不同的枚举:无作用域枚举(以 enum-key enum
声明)和有作用域枚举(以 enum-key enum class
或 enum struct
声明)。
目录 |
[编辑] 无作用域枚举
enum name { enumerator = constexpr , enumerator = constexpr , ... }
|
(1) | ||||||||
enum name : type { enumerator = constexpr , enumerator = constexpr , ... }
|
(2) | (C++11 起) | |||||||
enum name : type ;
|
(3) | (C++11 起) | |||||||
int
,除非枚举项的值不能放入 int
或 unsigned int
。若枚举项列表为空,则底层类型为如同枚举拥有单个值为 0 的枚举项)。每个 enumerator 都成为枚举的类型(即 name )的具名常量,在外围作用域可见,且可用于要求常量的任何位置。
每个枚举项都关联到一个底层类型的值。在 enumerator-list 中提供初始化器时,枚举项的值为那些初始化器所定义。若首个枚举项无初始化器,则关联值为零。对于任何其他定义无初始化器的枚举项,关联值为前一枚举项加一。
enum Foo { a, b, c = 10, d, e = 1, f, g = f + c }; //a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12
无作用域枚举类型的值可隐式转换到整数类型。若底层类型不固定,则值可转换到下列列表中能保有其整体值范围的首个类型: int 、 unsigned int 、 long 、 unsigned long 、 long long 或 unsigned long long 。若底层类型规定,则值可转换到其提升后的底层类型。
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
整数、浮点及其他枚举类型的值,可以转换到任何枚举类型,例如用 static_cast 。若值转换到枚举的底层类型后,在此枚举的范围外,则结果是未指定的 (C++17 前)未定义行为 (C++17 起)。若底层类型固定,则范围是底层类型的范围。若底层类型不固定,则范围是足够大以保有目标枚举所有枚举项的最小位域的所有可能值。注意这种转换后,值不需要等于为该类型定义的任何具名枚举项。
enum access_t { read = 1, write = 2, exec = 4 }; // 枚举项: 1, 2, 4 范围: 0..7 access_t rw = static_cast<access_t>(3); assert(rw & read && rw & write);
无作用域枚举的 name 可以忽略:这种声明仅引入枚举项到外围作用域:
enum { a, b, c = 0, d = a + 2 }; // 定义 a = 0, b = 1, c = 0, d = 2
当无作用域枚举是类成员时,其枚举项可用类成员访问运算符 .
和 ->
访问:
struct X { enum direction { left = 'l', right = 'r' }; }; X x; X* p = &x; int a = X::direction::left; // 仅于 C++11 及之后允许 int b = X::left; int c = x.left; int d = p->left;
有作用域枚举
1) 声明底层类型为 int 的有作用域枚举类型(关键词 class 与 struct 准确等同)
2) 声明底层类型为 type 的有作用域枚举类型
3) 底层类型为 int 的有作用域枚举类型的不可见枚举声明
4) 底层类型为 type 的有作用域枚举类型的不可见枚举声明
每个 enumerator 都成为该枚举的类型(即 name )的具名常量,它为枚举的作用域所含有,且能用作用域解析运算符访问。没有从有作用域枚举项到整数类型的隐式转换,尽管 static_cast 可用于获得枚举项的数值。 enum class Color { red, green = 20, blue }; Color r = Color::blue; switch(r) { case Color::red : std::cout << "red\n"; break; case Color::green: std::cout << "green\n"; break; case Color::blue : std::cout << "blue\n"; break; } // int n = r; // 错误:无有作用域枚举到 int 的转换 int n = static_cast<int>(r); // OK, n = 21 |
(C++11 起) |
底层类型固定的有作用域枚举类型和无作用域枚举类型,都能用列表初始化从整数初始化,而无需转型,若下列全部条件为真:
这使得引入新整数类型(例如 SafeInt ),并与其底层整数类型享受同样的既存调用约定可行,即使 ABI 表达上设计为不利于以值传递/返回结构体。 enum byte : unsigned char {}; // byte 是新整数类型 byte b { 42 }; // C++17 起 OK (直接列表初始化) byte c = { 42 }; // 错误 byte d = byte{ 42 }; // C++17 起 OK ;与 b 的值相同 byte e { -1 }; // 错误 struct A { byte b; }; A a1 = { { 42 } }; // 错误 A a2 = { byte{ 42 } }; // C++17 起 OK void f(byte); f({ 42 }); // 错误 enum class Handle : std::uint32_t { Invalid = 0 }; Handle h { 42 }; // C++17 起 OK |
(C++17 起) |
[编辑] 示例
#include <iostream> // 采用 16 位的 enum enum smallenum: int16_t { a, b, c }; // color 可为 red (值 0 )、 yellow (值 1 )、 green (值 20 )或 blue (值 21 ) enum color { red, yellow, green = 20, blue }; // altitude 可为 altitude::high 或 altitude::low enum class altitude: char { high='h', low='l', // C++11 允许尾随逗号 }; // 常量 d 为 0 ,常量 e 为 1 ,常量 f 为 3 enum { d, e, f = e + 2 }; // 枚举类型(有作用域和无作用域)能拥有重载的运算符 std::ostream& operator<<(std::ostream& os, color c) { switch(c) { case red : os << "red"; break; case yellow: os << "yellow"; break; case green : os << "green"; break; case blue : os << "blue"; break; default : os.setstate(std::ios_base::failbit); } return os; } std::ostream& operator<<(std::ostream& os, altitude al) { return os << static_cast<char>(al); } int main() { color col = red; altitude a; a = altitude::low; std::cout << "col = " << col << '\n' << "a = " << a << '\n' << "f = " << f << '\n'; }
输出:
col = red a = l f = 3
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
DR | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 1638 | C++14 | 不可见枚举声明的文法禁止用于模板特化 | 容许 nested-name-specifier |
[编辑] 参阅
枚举的 C 文档
|