C++11 における enum のまとめ
後で忘れた時に、すぐに思い出せるようにメモ。ソースはFDIS(N3290)です。正式な規格書が FDIS と異なっていたら、すみません。
2種類の enum
宣言(7.2p1)
実体宣言
unscoped enumeration は次のような感じ:
enum identifier1 : long { enumerator1, enumerator2, enumerator3 = 100, enumerator4, // このカンマは C++03 では ill-formed }
identifier1 は独立した型名として使える。省略しても可。: long は各 enumerator を表現するための、基盤となる型(underlying type)を指定している。省略すると(underlying type is not fixed)、実装依存の整数型が使用される(7.2p6)。
enumerator1 には定数 0 が対応し、enumerator3 のように明示的に指定されなければ、一つ前の enumerator を1増加させた値が対応する。つまり、以下のように対応する:
- enumerator1 … 0
- enumerator2 … 1
- enumerator3 … 100
- enumerator4 … 101
一方 scoped enumeration は次のような感じ:
enum class identifier2 : long { enumerator5, enumerator6, enumerator7 = 100, enumerator8, }
基本は unscoped enumeration に似ているが、異なる点がいくらかある。enum class は enum struct でもよい。これら2つは等価(7.2p2)。identifier2 は省略できない(7.2p2)。underlying type の指定を省略するとデフォルトで int が使用される(7.2p5)。
明示的に underlying type を指定してあったり、scoped enumeration でそれを省略した場合、underlying type は「fixed」であるという。逆に unscoped enumeration でそれを省略した場合には not fixed であり、実装依存の整数型が使用される。
前方宣言
unscoped enumeration でも unscoped enumeration でも C++11 から前方宣言が可能となった:
enum identifier1 : long;
または
enum class identifier2 : long;
identifier[12] は省略できない。underlying type の指定については、unscoped enumeration では省略できないが、scoped enumeration では省略できる(名無しさんご指摘ありがとうございました)。
もちろん、後で実体を再宣言することになる。scoped enumeration として前方宣言した後で、unscoped enumeration として再宣言したり、その逆をしてはならない(7.2p3)。また、再宣言する時に、前方宣言と矛盾するような underlying type の指定もできない(7.2p3)。
参照方法(7.2p10)
unscoped enumeration は次のような感じ:
identifier1 a = enumerator1; // OK.従来の使い方 identifier1 b = identifier1::enumerator3; // OK.C++11 で新しく認められた。C++03 では ill-formed
つまり、各 enumerator は、実体宣言を直接含むスコープでそのまま参照できる。また、C++11 では enum の型名で修飾して参照できるようになった。従来の enum は、実体宣言を直接含む名前空間に enumerator の名前をそのまま追加したので、名前衝突の危険性を持っていた。
一方 scoped enumeration は次のような感じ:
identifier2 c = enumerator5; // error identifier2 d = identifier2::enumerator7; // OK.この方法でしか各 enumerator にアクセスできない
つまり、各 enumerator は、enum の型名で修飾しないと参照できない。従来の enum とは異なり、実体宣言を直接含む名前空間を汚さず、名前衝突の危険性も無い。
型変換
「enum => 整数型」の変換(7.2p9)
unscoped enumeration では暗黙の型変換がある一方で、scoped enumeration では明示的にキャストしないと整数型に型変換できない。
unscoped enumeration では integral promotion(4.5) により標準変換(Standard conversions)、つまり暗黙の型変換が行われる:
int i = enumerator1; // OK.i に 0 が代入される
一方 scoped enumeration は次のような感じ:
int j = identifier2::enumerator5; // ill-formed.整数型への標準変換は無い int k = static_cast<int>( identifier2::enumerator5 ); // OK.static_cast を使えば,強制的に型変換できる(5.2.9p9)
「整数型 => enum」の変換(7.2p9)
列挙型は、他の型とは区別される、れっきとした型なので、列挙型であるX型のオブジェクトには、基本的にそのX型の enumerator しか代入できない。したがって、整数型の値を、列挙型の変数に代入することは基本的にできない。これは unscoped enumeration でも scoped enumeration でも同じ。どうしても型変換したい場合は、明示的にキャストするしかない。
unscoped enumeration の例:
identifier1 e = 0; // ill-formed identifier1 f = static_cast<identifier1>( 0 ); // OK.static_cast を使えば,強制的に型変換できる(5.2.9p10)
scoped enumeration の例(↑と同じ):
identifier2 g = 0; // ill-formed identifier2 h = static_cast<identifier2>( 0 ); // OK.static_cast を使えば,強制的に型変換できる(5.2.9p10)
異なる列挙型の変換
基本的に「整数型 => enum」の変換といっしょ。
列挙型は、他の型とは区別される、れっきとした型なので、列挙型であるX型のオブジェクトには、基本的にそのX型の enumerator しか代入できない。したがって、ある列挙型の値を、別の列挙型の変数に代入することは基本的にできない。どうしても型変換したい場合は、明示的に static_cast するしかない(5.2.9p10)。
その他
お腹へった…ご飯食べに行こう。