名前空間
変種
操作

列挙宣言

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
基本型
列挙型
関数型
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

列挙型は、値が値の範囲に制限された(詳細については以下を参照)、いくつかの明示的に名前を付けられた定数(「列挙子」)を含むことができる異なる型です。

定数の値は、列挙型の基底型として知られる整数型の値です。列挙型は、その基底型と同じサイズ値表現、およびアラインメント要件を持ちます。さらに、列挙型の各値は、基底型の対応する値と同じ表現を持ちます。

列挙型は、以下の構文を使用して(再)宣言されます

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以降)
1) decl-specifier-seq の中に現れるenum-specifier。これは列挙型とその列挙子を定義します。
2) enumerator-list の後にコンマを続けることができます。
3) 不完全な列挙型宣言: 列挙型を定義しますが、その列挙子は定義しません。この宣言の後、型は完全型であり、そのサイズは既知です。
enum-key -

enum

(C++11まで)

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

(C++11以降)
attr - (C++11以降) 0個以上の属性からなる、省略可能なシーケンス
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-specifierdecltype指定子で始めることはできません。

(C++11以降)
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以降)
1) 基底型が固定されていないスコープなし列挙型を宣言します(この場合、基底型は、すべての列挙子の値を表現できる実装定義の整数型であり、列挙子の値がintまたはunsigned 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によって定義されます。最初の列挙子が=を持たない場合、関連する値はゼロです。定義に=を持たない他の列挙子の場合、関連する値は前の列挙子の値に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;

メンバー宣言宣言指定子では、シーケンス

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以降)

列挙型は、以下のすべてが真である場合、キャストなしで整数からリスト初期化を使用して初期化できます。

  • 初期化は直接リスト初期化です。
  • 初期化子リストは単一の要素のみを含みます。
  • 列挙型は、スコープ付きであるか、基底型が固定されたスコープなしであるかのいずれかです。
  • 変換は縮小変換ではありません。

これにより、値渡し/戻り値の構造体でペナルティを課す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 宣言

using enum using-enum-declarator ; (C++20以降)
declarator (宣言子) - (おそらく修飾された)識別子または単純なテンプレート識別子


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 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

[編集] キーワード

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++標準に遡って適用されました。

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)
型が列挙型であるかをチェックする
(クラステンプレート) [編集]
型がスコープ付き列挙型であるかをチェックする
(クラステンプレート) [編集]
与えられた列挙型の基底となる整数型を取得する
(クラステンプレート) [編集]
列挙型をその基底となる型に変換する
(関数テンプレート) [編集]
English 日本語 中文(简体) 中文(繁體)