Enumeration declaration
列挙型( enumeration )は、値の範囲(詳細は後述)に制限された固有の型であり、複数の明示的に名前が付けられた定数(「 enumerator (列挙子)」)を含む場合があります。
定数の値は、列挙型の 基底型 として知られる整数型の値です。列挙型は、その基底型と同じ サイズ 、 値表現 、および アライメント要件 を持ちます。さらに、列挙型の各値は、基底型の対応する値と同じ表現を持ちます。
列挙型は以下の構文を使用して(再)宣言されます:
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 | - |
|
||||
| attr | - | (C++11以降) 任意の数の 属性 のオプションのシーケンス | ||||
| enum-head-name | - |
|
||||
| enum-base | - |
(C++11以降)
コロン (
:
) に続く
type-specifier-seq
。この列挙型の固定された基盤型として機能する整数型を指定する(CV修飾されている場合、修飾は無視される)
|
||||
| enumerator-list | - |
列挙子定義のカンマ区切りリスト。各定義は、単に一意の
identifier
(列挙子の名前となる)か、定数式を持つ一意の識別子:
identifier
=
constant-expression
のいずれか。
どちらの場合も、
identifier
の直後にオプションの
属性指定子シーケンス
を続けることができる。
(C++17以降)
|
列挙型には2つの異なる種類があります:
unscoped enumeration
(
enum-key
enum
で宣言される) と
scoped enumeration
(
enum-key
enum class
または
enum struct
で宣言される) です。
目次 |
スコープなし列挙型
enum
名前
(省略可能)
{
列挙子
=
定数式
,
列挙子
=
定数式
,
...
}
|
(1) | ||||||||
enum
名前
(省略可能)
:
型
{
列挙子
=
定数式
,
列挙子
=
定数式
,
...
}
|
(2) | (C++11以降) | |||||||
enum
名前
:
型
;
|
(3) | (C++11以降) | |||||||
各 enumerator は列挙型の名前付き定数(つまり name )となり、外側のスコープで可視化され、定数が必要なあらゆる場面で使用できます。
各列挙子は基底型の値に関連付けられます。
=
が
enumerator-list
内で指定されている場合、列挙子の値は関連付けられた
constant-expression
によって定義されます。最初の列挙子に
=
がない場合、関連付けられる値はゼロです。定義に
=
を持たないその他の列挙子については、関連付けられる値は前の列挙子の値に1を加えた値となります。
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 }; // 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;
|
メンバ宣言の 宣言指定子 において、以下のシーケンス
は常に列挙型宣言の一部として解析されます: struct S { enum E1 : int {}; enum E1 : int {}; // エラー: 列挙型の再宣言、 // enum E1型の長さゼロのビットフィールドとしては解析されない }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int'は基底型として解析されない } |
(C++11以降) |
リンケージ目的のための列挙型名
リンケージ目的のためのtypedef名を持たず、かつ列挙子を持つ無名の列挙型は、 typedef name for linkage purposes を持たず、かつ列挙子を持つ無名の列挙型は、 linkage purposes において、その基底型と最初の列挙子によって表される。このような列挙型は、 name for linkage purposes として列挙子を持つと言われる。
スコープ付き列挙型
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以降) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(注:指定されたテキストブロックには翻訳対象の自然言語テキストが含まれていないため、HTML構造のみを保持したまま出力しています)
|
列挙型は、以下のすべての条件が満たされる場合、キャストなしで整数から初期化できます( リスト初期化 を使用):
これにより、基盤となる整数型と同じ呼び出し規約を持つ新しい整数型(例:
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: orangeとappleをSに導入 }; void f() { S s; s.orange; // OK: fruit::orangeを指す S::orange; // OK: fruit::orangeを指す } 同じ名前の2つの列挙子を導入する2つの using enum 宣言は競合します。 enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // エラー: color::orangeとfruit::orangeが競合 } |
(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 }; // 列挙子: 1, 2, 4 範囲: 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); // CWG 1766以降未定義動作 access_t y = static_cast<access_t>(8); // CWG 1766以降未定義動作 enum foo { a = 0, b = UINT_MAX }; // 範囲: [0, UINT_MAX] foo x = foo(-1); // CWG 1766以降未定義動作、 // fooの基となる型がunsigned intの場合でも
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_enumerator_attributes
|
201411L
|
(C++17) | 属性 列挙子の属性 |
__cpp_using_enum
|
201907L
|
(C++20) |
using enum
|
キーワード
例
#include <cstdint> #include <iostream> // 16ビットを使用する列挙型 enum smallenum: std::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', // 末尾のカンマはCWG 518以降でのみ許可 }; // 定数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); } // スコープ付き列挙型(C++11)は以前のC++リビジョンで部分的にエミュレート可能: enum struct E11 { x, y }; // C++11以降 struct E98 { enum { x, y }; }; // C++11以前で使用可能 namespace N98 { enum { x, y }; } // C++11以前で使用可能 struct S98 { static const int x = 0, y = 1; }; // 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++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 377 | C++98 | すべての列挙子の値を表現できる整数型が存在しない場合の動作が未規定 | この場合、列挙型は不適格 |
| CWG 518 | C++98 | 列挙子リストの末尾にカンマを置くことが許可されていなかった | 許可される |
| CWG 1514 | C++11 | 基底型が固定された列挙型の再定義が、クラスメンバ宣言のビットフィールドとして解析される可能性があった | 常に再定義として解析される |
| CWG 1638 | C++11 | 不透明列挙宣言の文法がテンプレート特殊化での使用を禁止していた | 修飾名指定子が許可される |
| CWG 1766 | C++98 | 範囲外の値を基底型が固定されていない列挙型にキャストした結果が未規定 | 動作は未定義 |
| CWG 1966 | C++11 |
CWG issue 1514
の解決により、条件演算子の
:
が
enum-base
の一部と見なされるようになった
|
解決をメンバ宣言指定子にのみ適用 |
| CWG 2156 | C++11 | 列挙型定義がusing宣言を使用して列挙型を定義できた | 禁止される |
| CWG 2157 | C++11 | CWG issue 1966 の解決が修飾された列挙名をカバーしていなかった | カバーされる |
| CWG 2530 | C++98 | 列挙子リストに同じ識別子を持つ複数の列挙子を含めることができた | 禁止される |
| CWG 2590 | C++98 | 列挙型のサイズ、値表現、アラインメント要件が基底型に依存していなかった | すべて基底型と同じになる |
| CWG 2621 | C++20 | using enum 宣言で使用される列挙名のルックアップが不明確 | 明確化された |
| CWG 2877 | C++20 | 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 documentation
for
列挙型
|
|