次の方法で共有


列挙型 (C++)

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

Note

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

構文

enum-name=
identifier

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

enum-head=
enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt

enum-head-name=
nested-name-specifieropt identifier

opaque-enum-declaration=
enum-key attribute-specifier-seq optenum-head-name enum-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=
identifier attribute-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
宣言内でこのキーワードを使用することにより、列挙型がスコープを持ち、identifier が提供される必要があることを指定します。 class の代わりに struct キーワードを使用することもできます。これらはこのコンテキストでは意味的に同等です。

列挙子スコープ

列挙型は、名前付き定数として表される値の範囲を記述するコンテキストを提供します。 これらの名前付き定数は "列挙子" とも呼ばれます。 元の 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 };

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

キャストの規則

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

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 の列挙型宣言
キーワード