名稱空間
變體
操作

列舉宣告

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
表示式
替代表示
字面量
布林字面量 - 整數字面量 - 浮點字面量
字元字面量 - 字串字面量 - nullptr (C++11)
使用者定義 (C++11)
工具
屬性 (C++11)
型別
typedef 宣告
類型別名宣告 (C++11)
型別轉換
記憶體分配
類特有的函式屬性
explicit (C++11)
static

特殊成員函式
模板
雜項
 
 

“列舉”是一種獨特的型別,其值被限制在一定範圍之內(詳情見下文),該範圍可能包含若干顯式命名的常量(“列舉器”)。

常量的數值是整數型別的值,這種整數型別被稱為列舉的“底層型別”。列舉的大小值表示對齊要求與其底層型別相同。此外,列舉的每個值與底層型別的對應值具有相同的表示。

列舉透過以下語法進行(重)宣告:

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 起)
1) enum-specifier,出現在宣告語法中的 decl-specifier-seq:定義列舉型別及其列舉器。
2) 可以在 enumerator-list 後加一個尾隨逗號。
3) 不透明列舉宣告:定義列舉型別但不定義其列舉器:在此宣告之後,該型別是完整型別且其大小已知。
enum-key -

enum

(C++11 前)

enumenum classenum struct 之一

(C++11 起)
屬性 - (C++11 起) 可選的任意數量屬性序列
enum-head-name -

正在宣告的列舉的名稱,可以省略。

(C++11 前)

正在宣告的列舉的名稱,可選地前面帶有一個 nested-name-specifier:由名稱和作用域解析運算子 :: 組成的序列,以作用域解析運算子結尾。它只能在無作用域的非不透明列舉宣告中省略。
僅當列舉名稱存在且此宣告是重新宣告時,才允許出現 nested-name-specifier。對於不透明列舉宣告,nested-name-specifier 只能在顯式特化宣告中列舉名稱之前出現。
如果存在 nested-name-specifier,則 enum-specifier 不能引用僅透過繼承或using declaration引入的列舉,並且 enum-specifier 只能出現在包含先前宣告的名稱空間中。在這種情況下,nested-name-specifier 不能以 decltype 說明符開頭。

(C++11 起)
enum-base - (C++11 起) 冒號 (:),後跟一個 type-specifier-seq,它指定一個整數型別(如果它是 cv 限定的,則忽略限定符),該整數型別將作為此列舉型別的固定底層型別
enumerator-list - 逗號分隔的列舉器定義列表,每個定義要麼是唯一的 identifier,成為列舉器的名稱,要麼是帶常量表達式的唯一識別符號:identifier = constant-expression在這兩種情況下,identifier 之後都可以直接跟著一個可選的屬性說明符序列(C++17 起)

有兩種不同種類的列舉:無作用域列舉(用 enum-key enum 宣告)和 有作用域列舉(用 enum-key enum classenum 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 起)
1) 宣告一個無作用域列舉型別,其底層型別不固定(在這種情況下,底層型別是一個實現定義的整數型別,可以表示所有列舉器值;除非某個列舉器的值無法放入 intunsigned int 中,否則該型別不會大於 int。如果 enumerator-list 為空,則底層型別就好像列舉有一個值為 0 的列舉器。如果沒有整數型別可以表示所有列舉器值,則列舉是病態的)。
2) 宣告一個無作用域列舉型別,其底層型別是固定的。
3) 無作用域列舉的不透明列舉宣告必須指定名稱和底層型別。

每個 enumerator 都會成為列舉型別(即 name)的一個命名常量,在包含作用域中可見,並且可以在需要常量時使用。

enum Color { red, green, blue };
Color r = red;
 
switch(r)
{
    case red  : std::cout << "red\n";   break;
    case green: std::cout << "green\n"; break;
    case blue : std::cout << "blue\n";  break;
}

每個列舉器都與底層型別的一個值相關聯。當 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;

宣告說明符中,對於成員宣告,序列

enum enum-head-name :

總是解析為列舉宣告的一部分

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 名稱且具有列舉器的未命名列舉,由其底層型別和其第一個列舉器表示;這樣的列舉據說具有一個列舉器作為“用於連結目的的名稱”。

[編輯] 有作用域列舉

enum struct|class name { enumerator = constant-expression , enumerator = constant-expression , ... } (1)
enum struct|class name : type { enumerator = constant-expression , enumerator = constant-expression , ... } (2)
enum struct|class name ; (3)
enum struct|class name : type ; (4)
1) 宣告一個有作用域的列舉型別,其底層型別為 int(關鍵字 classstruct 完全等價)
2) 宣告一個有作用域的列舉型別,其底層型別為 type
3) 宣告一個有作用域的列舉型別的不透明列舉,其底層型別為 int
4) 宣告一個有作用域的列舉型別的不透明列舉,其底層型別為 type

每個 enumerator 成為列舉型別(即 name)的命名常量,包含在列舉的作用域內,並且可以使用作用域解析運算子訪問。沒有從有作用域列舉器值到整數型別的隱式轉換,儘管可以使用 static_cast 獲取列舉器的數值。

#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 宣告

using enum using-enum-declarator ; (C++20 起)
宣告符 - 一個(可能合格的)識別符號簡單模板識別符號


declarator 必須命名一個非依賴列舉型別。列舉宣告透過僅限型別的普通限定非限定查詢找到,具體取決於 declarator 是否限定。

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

[編輯] 關鍵詞

enum, struct, class, using

[編輯] 示例

#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 文件 關於 列舉