列舉宣告
“列舉”是一種獨特的型別,其值被限制在一定範圍之內(詳情見下文),該範圍可能包含若干顯式命名的常量(“列舉器”)。
常量的數值是整數型別的值,這種整數型別被稱為列舉的“底層型別”。列舉的大小、值表示和對齊要求與其底層型別相同。此外,列舉的每個值與底層型別的對應值具有相同的表示。
列舉透過以下語法進行(重)宣告:
enum-key attr (可選) enum-head-name (可選) enum-base (可選){ enumerator-list (可選) } |
(1) | ||||||||
enum-key attr (可選) enum-head-name (可選) enum-base (可選){ enumerator-list , } |
(2) | ||||||||
enum-key attr (可選) enum-head-name enum-base (可選) ; |
(3) | (C++11 起) | |||||||
enum-key | - |
| ||||
屬性 | - | (C++11 起) 可選的任意數量屬性序列 | ||||
enum-head-name | - |
| ||||
enum-base | - | (C++11 起) 冒號 (: ),後跟一個 type-specifier-seq,它指定一個整數型別(如果它是 cv 限定的,則忽略限定符),該整數型別將作為此列舉型別的固定底層型別 | ||||
enumerator-list | - | 逗號分隔的列舉器定義列表,每個定義要麼是唯一的 identifier,成為列舉器的名稱,要麼是帶常量表達式的唯一識別符號:identifier = constant-expression。在這兩種情況下,identifier 之後都可以直接跟著一個可選的屬性說明符序列。(C++17 起) |
有兩種不同種類的列舉:無作用域列舉(用 enum-key enum
宣告)和 有作用域列舉(用 enum-key enum class
或 enum struct
宣告)。
目錄 |
[編輯] 無作用域列舉
enum name (可選) { enumerator = constant-expression , enumerator = constant-expression , ... } |
(1) | ||||||||
enum name (可選) : type { enumerator = constant-expression , enumerator = constant-expression , ... } |
(2) | (C++11 起) | |||||||
enum name : type ; |
(3) | (C++11 起) | |||||||
每個 enumerator 都會成為列舉型別(即 name)的一個命名常量,在包含作用域中可見,並且可以在需要常量時使用。
每個列舉器都與底層型別的一個值相關聯。當 enumerator-list 中提供了 =
時,列舉器的值由那些關聯的 constant-expression 定義。如果第一個列舉器沒有 =
,則關聯值為零。對於任何其他沒有 =
定義的列舉器,關聯值為前一個列舉器的值加一。
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
無作用域列舉的 name 可以省略:這樣的宣告只將列舉器引入到包含作用域中。
enum { a, b, c = 0, d = a + 2 }; // defines 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; // allowed only in C++11 and later int b = X::left; int c = x.left; int d = p->left;
總是解析為列舉宣告的一部分 struct S { enum E1 : int {}; enum E1 : int {}; // error: redeclaration of enumeration, // NOT parsed as a zero-length bit-field of type enum E1 }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int' is NOT parsed as the underlying type } |
(C++11 起) |
[編輯] 用於連結的列舉名稱
對於連結目的,沒有用於連結目的的 typedef 名稱且具有列舉器的未命名列舉,由其底層型別和其第一個列舉器表示;這樣的列舉據說具有一個列舉器作為“用於連結目的的名稱”。
[編輯] 有作用域列舉
1) 宣告一個有作用域的列舉型別,其底層型別為 int(關鍵字 class 和 struct 完全等價)
2) 宣告一個有作用域的列舉型別,其底層型別為 type
3) 宣告一個有作用域的列舉型別的不透明列舉,其底層型別為 int
4) 宣告一個有作用域的列舉型別的不透明列舉,其底層型別為 type
每個 enumerator 成為列舉型別(即 name)的命名常量,包含在列舉的作用域內,並且可以使用作用域解析運算子訪問。沒有從有作用域列舉器值到整數型別的隱式轉換,儘管可以使用 執行此程式碼 #include <iostream> int main() { 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; // error: no implicit conversion from scoped enum to int int n = static_cast<int>(r); // OK, n = 21 std::cout << n << '\n'; // prints 21 } |
(C++11 起) |
如果滿足以下所有條件,則可以使用列表初始化從整數初始化列舉,無需強制轉換:
這使得可以引入新的整數型別(例如 `SafeInt`),即使在懲罰透過值傳遞/返回結構的 ABI 上,它們也享有與其底層整數型別相同的現有呼叫約定。 enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17) byte b{42}; // OK as of C++17 (direct-list-initialization) byte c = {42}; // error byte d = byte{42}; // OK as of C++17; same value as b byte e{-1}; // error struct A { byte b; }; A a1 = {{42}}; // error (copy-list-initialization of a constructor parameter) A a2 = {byte{42}}; // OK as of C++17 void f(byte); f({42}); // error (copy-list-initialization of a function parameter) enum class Handle : std::uint32_t { Invalid = 0 }; Handle h{42}; // OK as of C++17 |
(C++17 起) |
using enum 宣告
enum E { x }; void f() { int E; using enum E; // OK } using F = E; using enum F; // OK template<class T> using EE = T; void g() { using enum EE<E>; // OK } 一個 using enum 宣告引入命名列舉的列舉器名稱,如同透過每個列舉器的using 宣告一樣。當在類作用域中時,using enum 宣告將命名列舉的列舉器作為成員新增到作用域中,使其可用於成員查詢。 enum class fruit { orange, apple }; struct S { using enum fruit; // OK: introduces orange and apple into S }; void f() { S s; s.orange; // OK: names fruit::orange S::orange; // OK: names fruit::orange } 兩個 using enum 宣告如果引入了兩個同名的列舉器,則會發生衝突。 enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // error: color::orange and fruit::orange conflict } |
(C++20 起) |
[編輯] 注意
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
整數、浮點和列舉型別的值可以透過使用 static_cast
轉換為任何列舉型別。請注意,這種轉換後的值不一定等於為列舉定義的任何命名列舉器
enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7 access_t rwe = static_cast<access_t>(7); assert((rwe & read) && (rwe & write) && (rwe & exec)); access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766 access_t y = static_cast<access_t>(8); // undefined behavior since CWG 1766 enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX] foo x = foo(-1); // undefined behavior since CWG 1766, // even if foo's underlying type is unsigned int
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_enumerator_attributes |
201411L |
(C++17) | 列舉器的屬性 |
__cpp_using_enum |
201907L |
(C++20) | using enum
|
[編輯] 關鍵詞
[編輯] 示例
#include <cstdint> #include <iostream> // enum that takes 16 bits enum smallenum: std::int16_t { a, b, c }; // color may be red (value 0), yellow (value 1), green (value 20), or blue (value 21) enum color { red, yellow, green = 20, blue }; // altitude may be altitude::high or altitude::low enum class altitude: char { high = 'h', low = 'l', // trailing comma only allowed after CWG 518 }; // the constant d is 0, the constant e is 1, the constant f is 3 enum { d, e, f = e + 2 }; // enumeration types (both scoped and unscoped) can have overloaded operators 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); } // The scoped enum (C++11) can be partially emulated in earlier C++ revisions: enum struct E11 { x, y }; // since C++11 struct E98 { enum { x, y }; }; // OK in pre-C++11 namespace N98 { enum { x, y }; } // OK in pre-C++11 struct S98 { static const int x = 0, y = 1; }; // OK in pre-C++11 void emu() { std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4 } namespace cxx20 { enum class long_long_long_name { x, y }; void using_enum_demo() { std::cout << "C++20 `using enum`: __cpp_using_enum == "; switch (auto rnd = []{return long_long_long_name::x;}; rnd()) { #if defined(__cpp_using_enum) using enum long_long_long_name; case x: std::cout << __cpp_using_enum << "; x\n"; break; case y: std::cout << __cpp_using_enum << "; y\n"; break; #else case long_long_long_name::x: std::cout << "?; x\n"; break; case long_long_long_name::y: std::cout << "?; y\n"; break; #endif } } } int main() { color col = red; altitude a; a = altitude::low; std::cout << "col = " << col << '\n' << "a = " << a << '\n' << "f = " << f << '\n'; cxx20::using_enum_demo(); }
可能的輸出
col = red a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 377 | C++98 | 當沒有整數型別可以表示所有列舉器值時,行為未指定 在這種情況下,列舉是不完善的 |
在這種情況下,列舉是病態的 在這種情況下,列舉是病態的 |
CWG 518 | C++98 | 列舉器列表後不允許尾隨逗號 | 允許 |
CWG 1514 | C++11 | 固定底層型別的列舉的重新定義 可能被解析為類成員宣告中的位域 |
總是解析為重新定義 |
CWG 1638 | C++11 | 不透明列舉宣告的語法 禁止用於模板特化 |
nested-name-specifier 允許 |
CWG 1766 | C++98 | 將超出範圍的值轉換為沒有固定底層型別的列舉 其結果未指定 |
行為未定義 |
CWG 1966 | C++11 | CWG 問題 1514 的解決方案使得條件表示式中的 : 成為 enum-base 的一部分 |
僅將解決方案應用於 成員宣告說明符 |
CWG 2156 | C++11 | 列舉定義可以透過 using-宣告來定義列舉型別 列舉型別可以透過 using-宣告來定義 |
已禁止 |
CWG 2157 | C++11 | CWG 問題 1966 的解決方案 未涵蓋限定列舉名稱 |
已涵蓋 |
CWG 2530 | C++98 | 一個列舉器列表可能包含多個 具有相同識別符號的列舉器 |
已禁止 |
CWG 2590 | C++98 | 列舉的大小、值表示和對齊要求 不依賴於其底層型別 |
所有這些都與 底層型別相同 |
CWG 2621 | C++20 | using enum 宣告中使用的列舉名稱查詢不明確 using enum 宣告中使用的列舉名稱查詢不明確 |
已明確 |
CWG 2877 | C++20 | using enum 宣告中使用的列舉名稱查詢不明確 using enum 宣告並非僅限於型別 |
已改為僅限型別 |
[編輯] 參考
- C++23 標準 (ISO/IEC 14882:2024)
- 9.7.1 列舉宣告 [dcl.enum]
- C++20 標準 (ISO/IEC 14882:2020)
- 9.7.1 列舉宣告 [dcl.enum]
- C++17 標準 (ISO/IEC 14882:2017)
- 10.2 列舉宣告 [dcl.enum]
- C++14 標準 (ISO/IEC 14882:2014)
- 7.2 列舉宣告 [dcl.enum]
- C++11 標準 (ISO/IEC 14882:2011)
- 7.2 列舉宣告 [dcl.enum]
- C++03 標準 (ISO/IEC 14882:2003)
- 7.2 列舉宣告 [dcl.enum]
- C++98 標準 (ISO/IEC 14882:1998)
- 7.2 列舉宣告 [dcl.enum]
[編輯] 參見
(C++11) |
檢查一個型別是否是列舉型別 (類模板) |
(C++23) |
檢查一個型別是否是有作用域的列舉型別 (類模板) |
(C++11) |
獲取給定列舉型別的底層整數型別 (類模板) |
(C++23) |
將列舉轉換為其底層型別 (函式模板) |
C 文件 關於 列舉
|