Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

列挙型( 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以降)
1) enum-specifier 、これは decl-specifier-seq 内に現れ、 declaration 構文の一部を成す:列挙型とその列挙子を定義する。
2) 列挙子リストの後には末尾のコンマを付けることができます。
3) 不透明enum宣言:列挙型を定義するがその列挙子は定義しない。この宣言後、型は完全型となりそのサイズが既知となる。
enum-key -

enum

(C++11まで)

enum , enum class , または enum struct のいずれか

(C++11以降)
attr - (C++11以降) 任意の数の 属性 のオプションのシーケンス
enum-head-name -

宣言されている列挙型の名前。省略可能。

(C++11まで)

宣言されている列挙型の名前。オプションで nested-name-specifier (名前とスコープ解決演算子 :: のシーケンスで、スコープ解決演算子で終わる)を前に付けることができる。非スコープ付き非不透明列挙型宣言でのみ省略可能。
nested-name-specifier は、列挙型名が存在し、この宣言が再宣言である場合にのみ現れることができる。不透明列挙型宣言では、 nested-name-specifier 明示的特殊化宣言 でのみ列挙型名の前に現れることができる。
nested-name-specifier が存在する場合、 enum-specifier は、 using 宣言 によって単に継承または導入された列挙型を参照することはできず、 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以降)

列挙型には2つの異なる種類があります: unscoped enumeration ( enum-key enum で宣言される) と scoped enumeration ( enum-key enum class または enum struct で宣言される) です。

目次

スコープなし列挙型

enum 名前  (省略可能) { 列挙子 = 定数式 , 列挙子 = 定数式 , ... } (1)
enum 名前  (省略可能) : { 列挙子 = 定数式 , 列挙子 = 定数式 , ... } (2) (C++11以降)
enum 名前 : ; (3) (C++11以降)
1) 基となる型が固定されていないスコープなし列挙型を宣言する(この場合、基となる型はすべての列挙子の値を表現できる実装定義の整数型であり、列挙子の値が int または unsigned int に収まらない場合を除き、この型は int より大きくならない。 enumerator-list が空の場合、基となる型は値 0 を持つ単一の列挙子を持つ列挙型の場合と同様である。すべての列挙子の値を表現できる整数型が存在しない場合、その列挙型は不適格となる)。
2) 基になる型が固定されたスコープなし列挙型を宣言します。
3) スコープなし列挙型に対する不透明enum宣言は、名前と基底型を指定しなければなりません。

enumerator は列挙型の名前付き定数(つまり name )となり、外側のスコープで可視化され、定数が必要なあらゆる場面で使用できます。

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

各列挙子は基底型の値に関連付けられます。 = 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;

メンバ宣言の 宣言指定子 において、以下のシーケンス

enum enum-head-name :

は常に列挙型宣言の一部として解析されます:

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 として列挙子を持つと言われる。

スコープ付き列挙型

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 (キーワード class struct は完全に等価)
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以降)

(注:指定されたテキストブロックには翻訳対象の自然言語テキストが含まれていないため、HTML構造のみを保持したまま出力しています)

列挙型は、以下のすべての条件が満たされる場合、キャストなしで整数から初期化できます( リスト初期化 を使用):

  • 初期化が直接リスト初期化であること。
  • 初期化子リストが単一の要素のみを持つこと。
  • 列挙型がスコープ付き、または基盤型が固定されたスコープなし列挙型であること。
  • 変換が縮小変換ではないこと。

これにより、基盤となる整数型と同じ呼び出し規約を持つ新しい整数型(例: 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以降)
宣言子 - (修飾された可能性のある) 識別子 または 単純テンプレート識別子


宣言子 は非 依存 な列挙型を指定しなければなりません。列挙宣言は、型のみによる通常の 修飾付き または 修飾なし の名前探索によって見つけられます。これは 宣言子 が修飾されているかどうかに依存します。

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

キーワード

enum struct class using

#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 documentation for 列挙型