列挙型 (C++)

列挙型は、列挙子呼ばれる名前付き整数定数のセットで構成されるユーザー定義型です。

Note

この記事では、ISO 標準 C++ 言語 enum の型と、C++11 で導入されたスコープ (または厳密に型指定された) enum class 型について説明します。 C++/CLI および C++/CX の型の詳細public enum classについては、(C++/CLI および C++/CX) を参照してくださいenum classprivate enum class

構文

enum-name:
identifier

enum-specifier:
enum-head{enumerator-listopt}
enum-head { enumerator-list , }

enum-head:
enum-keyattribute-specifier-seqoptenum-head-nameoptenum-baseopt

enum-head-name:
nested-name-specifieroptidentifier

opaque-enum-declaration:
enum-keyattribute-specifier-seqoptenum-head-nameenum-baseopt;

enum-key:
enum
enum class
enum struct

enum-base:
: type-specifier-seq

enumerator-list:
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition:
enumerator
enumerator = constant-expression

enumerator:
identifierattribute-specifier-seqopt

使用方法

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

パラメーター

identifier
列挙体に渡す型名。

type
列挙子の基になる型であり、すべての列挙子は同じ型を基にしています。 任意の整数型を指定できます。

enum-list
列挙体に含まれる列挙子のコンマ区切りのリスト。 スコープ内のすべての列挙子または変数名は一意である必要があります。 ただし、値は複製できます。 スコープなし列挙型では、スコープは周囲のスコープです。スコープ付き列挙型では、スコープはそれ自体です enum-list 。 スコープ付き列挙型では、リストが空の場合があり、実際には新しい整数型が定義されます。

class
宣言でこのキーワード (keyword)を使用して、列挙型のスコープを指定し、指定identifierする必要があります。 このコンテキストでは意味的にstruct同等であるため、キーワード (keyword)をclass代わりに使用することもできます。

列挙子スコープ

列挙型は、名前付き定数として表される値の範囲を記述するコンテキストを提供します。 これらの名前付き定数は、列挙子とも呼ばれます。 元の C 型と C++ enum 型では、非修飾列挙子は宣言されているスコープ全体で enum 表示されます。 スコープ付き列挙型では、列挙子名は型名で修飾する enum 必要があります。 次の例に、2 種類の列挙型のこの基本的な相違点を示します。

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

列挙型の各値の名前には、列挙型の値の順序に対応する場所の整数値が割り当てられます。 既定では、最初の値には 0、その次の値には 1 などのように割り当てられますが、次に示すように、列挙体の値を明示的に設定することもできます。

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

Diamonds 列挙子に値 1 を割り当てます。 後続の列挙子に明示的な値が指定されていない場合は、前の列挙子の値に 1 を加えた値を受け取ります。 前の例では、Hearts の値が 2、Clubs の値が 3 などのようになります。

すべての列挙子は定数として扱われ、enum が定義されているスコープ内で (スコープを持たない列挙型の場合)、または enum 自体のスコープ内で (スコープを持つ列挙型の場合)、列挙子の名前は一意である必要があります。 名前に指定された値は一意である必要はありません。 たとえば、スコープなし列挙型 Suitの次の宣言を考えてみましょう。

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

HeartsClubsSpadesDiamondsは、それぞれ 5、6、4、5 です。 5 が複数回使用されていることに注意してください。意図されていない場合でも許可されます。 これらの規則はスコープを持つ列挙型でも同じです。

キャストの規則

スコープを持たない列挙型の定数が暗黙的に int に変換されることはありますが、int が列挙型の値に暗黙的に変換されることはありません。 次の例は、次のような値を割り当て hand ようとした場合の動作を Suit示しています。

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

スコープを持つまたは持たない列挙子に int を変換するには、キャストが必要です。 ただし、スコープ外の列挙子を、キャストなしで整数値に昇格させることができます。

int account_num = Hearts; //OK if Hearts is in an unscoped enum

このような暗黙の型変換を使用すると、意図しない副作用につながることがあります。 スコープを持たない列挙型に関連するプログラミング エラーをなくすために役立つように、スコープを持つ列挙型の値は厳密に型指定します。 次の例に示すように、スコープ付き列挙子は列挙型名 (識別子) で修飾する必要があり、暗黙的に変換することはできません。

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

hand = account_num; 行では、前に示したように、スコープを持たない列挙型に関連するエラーが発生することに注意してください。 明示的なキャストで許可されます。 ただし、スコープを持つ列挙型を使用しても、次のステートメント、account_num = Suit::Hearts; での変換の試みは、明示的なキャストなしではエラーが発生します。

列挙子がない列挙型

Visual Studio 2017 バージョン 15.3 以降 (/std:c++17 以降で使用可能): 明示的な基になる型と列挙子を使用して列挙型 (標準またはスコープ) を定義することで、他の型への暗黙的な変換を持つ新しい整数型を実際に導入できます。 組み込みの基になる型ではなく、この型を使用することで、不注意による暗黙的な変換によって発生する微妙なエラーの可能性を排除できます。

enum class byte : unsigned char { };

新しい型は基になる型の正確なコピーであり、呼び出し規則が同じであるため、パフォーマンスを低下させずに、ABIs 全体で使用できます。 型の変数が直接リスト初期化を使用して初期化される場合、キャストは必要ありません。 次の例は、さまざまなコンテキストで列挙子を使用して列挙子を使用して列挙型を初期化する方法を示しています。

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

関連項目

C 列挙宣言
キーワード