列挙宣言
列挙型は、値が値の範囲に制限された(詳細については以下を参照)、いくつかの明示的に名前を付けられた定数(「列挙子」)を含むことができる異なる型です。
定数の値は、列挙型の基底型として知られる整数型の値です。列挙型は、その基底型と同じサイズ、値表現、およびアラインメント要件を持ちます。さらに、列挙型の各値は、基底型の対応する値と同じ表現を持ちます。
列挙型は、以下の構文を使用して(再)宣言されます
enum-key attr (optional) enum-head-name (optional) enum-base (optional){ enumerator-list (optional) } |
(1) | ||||||||
enum-key attr (optional) enum-head-name (optional) enum-base (optional){ enumerator-list , } |
(2) | ||||||||
enum-key attr (optional) enum-head-name enum-base (optional) ; |
(3) | (C++11以降) | |||||||
| enum-key | - |
| ||||
| attr | - | (C++11以降) 0個以上の属性からなる、省略可能なシーケンス | ||||
| enum-head-name | - |
| ||||
| enum-base | - | (C++11以降) コロン (:) の後に、この列挙型のために固定された基底型として機能する整数型を指定するtype-specifier-seq(cv修飾されている場合、修飾は無視されます)が続きます。 | ||||
| enumerator-list | - | 列挙子定義のコンマ区切りリストで、それぞれが単に一意なidentifier(列挙子の名前になる)か、定数式を伴う一意な識別子(identifier = constant-expression)です。いずれの場合も、identifierの直後にオプションの属性指定子シーケンスを続けることができます。(C++17以降) |
列挙型には2つの異なる種類があります。スコープなし列挙型(enum-key enum で宣言される)とスコープ付き列挙型(enum-key enum class または enum struct で宣言される)です。
目次 |
[編集] スコープなし列挙型
enum name (optional) { enumerator = constant-expression , enumerator = constant-expression , ... } |
(1) | ||||||||
enum name (optional) : type { enumerator = constant-expression , enumerator = constant-expression , ... } |
(2) | (C++11以降) | |||||||
enum name : type ; |
(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 }; // 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以降) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
列挙型は、以下のすべてが真である場合、キャストなしで整数からリスト初期化を使用して初期化できます。
これにより、値渡し/戻り値の構造体でペナルティを課すABIであっても、基底の整数型と同じ既存の呼び出し規約を享受する新しい整数型(例:`SafeInt`)を導入することが可能になります。 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 declarationによって導入されたかのように導入します。クラススコープにある場合、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 } 同じ名前の2つの列挙子を導入する2つの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++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| 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 issue 1514の解決により、 条件式の :がenum-baseの一部になりました。 |
解決策を メンバー宣言指定子にのみ適用します。 |
| CWG 2156 | C++11 | 列挙型の定義が using-declarationsを使用して列挙型を定義できました。 |
禁止された |
| 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 宣言で使用される using enum 宣言は型のみではありませんでした。 |
型のみにしました。 |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 9.7.1 列挙宣言 [dcl.enum]
- C++20 standard (ISO/IEC 14882:2020)
- 9.7.1 列挙宣言 [dcl.enum]
- C++17 standard (ISO/IEC 14882:2017)
- 10.2 列挙宣言 [dcl.enum]
- C++14 standard (ISO/IEC 14882:2014)
- 7.2 列挙宣言 [dcl.enum]
- C++11 standard (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 ドキュメント: 列挙型
| |