列挙型は、 列挙子と呼ばれる名前付き整数定数のセットで構成されるユーザー定義型です。
注
この記事では、ISO 標準 C++ 言語 enum 型と、C++11 で導入されたスコープ (または厳密に型指定された) enum class 型について説明します。 C++/CLI および C++/CX の public enum class 型または private enum class 型の詳細については、 enum class (C++/CLI および C++/CX) を参照してください。
構文
enum-name:
identifier
enum-specifier:
enum-head
{
enumerator-list
選ぶ}
enum-head
{
enumerator-list
,
}
enum-head:
enum-key
attribute-specifier-seq
optenum-head-nameoptenum-base選ぶ
enum-head-name:
nested-name-specifier
選ぶidentifier
opaque-enum-declaration:
enum-key
attribute-specifier-seq
optenum-head-nameenum-base選ぶ;
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-seq
選ぶ
使用方法
// 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 };
Diamonds、Hearts、Clubs、およびSpadesの値はそれぞれ 5、6、4、5 です。 5 が複数回使用されていることに注意してください。意図されていない場合でも許可されます。 これらの規則は、スコープ付き列挙型でも同じです。
キャストの規則
スコープなし列挙型定数は暗黙的に intに変換できますが、 int は列挙型の値に暗黙的に変換できません。 次の例は、Suitではない値hand割り当てようとした場合の動作を示しています。
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 { };
新しい型は、基になる型の正確なコピーであるため、同じ呼び出し規則を持っています。つまり、パフォーマンスを低下させることなく、AVI 間で使用できます。 型の変数が直接リスト初期化を使用して初期化される場合、キャストは必要ありません。 次の例は、さまざまなコンテキストで列挙子なしで列挙型を初期化する方法を示しています。
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;
}