ここまでは、主に数値と文字列を扱ってきました。 現実世界をモデル化するには、変数が取りうる値を限られた数だけにしたいことがあります。 それぞれに名前が付いた、いくつかの異なる値だけを持つ専用の型が欲しくなるかもしれません。 たとえばスケートボードの工場なら、デッキの素材をメープル、竹、プラスチックの3つだけから選ぶようにしたいはずです。
これらの値を整数で表現することもできますが、素材としてシステムから不正な値が渡ってきていないかを確認するコードを別途書かなければなりません。
そうしたマジックナンバーは、その意味をソースコードからたどるのが難しく、取り違えも起こりがちです。
enumerationsを使うと、意図が伝わりやすいコードを促し、意図しない比較ミスを防ぐことができます。
この種の列挙型を指す専門用語はscoped enumerationです。
以下のコードは、DeckMaterialというenumerationの書き方を示しています。
enum classキーワードと、定義の末尾にある;に注目してください。
enum class DeckMaterial {
maple,
bamboo,
plastic
};
次に、スケートショップの価格計算関数を見て、enumerationのenumeratorを指定しているスコープ解決演算子(::)に注目してください。
double deck_price(double base_price, DeckMaterial material) {
if(material == DeckMaterial::plastic) {
return base_price * 0.9;
}
return base_price * 1.3;
}
ホイールの素材を表す2つ目のenumerationがあるとします。
enum class WheelMaterial {
steel,
clay,
plastic
};
ホイールとデッキはどちらも_プラスチック_で作ることができますが、この2つを混同することはできません。
これらは別の_型_だからです。DeckMaterialのプラスチックと、WheelMaterialのプラスチックです。
それぞれのenumerationは、enumeratorsを自分自身のスコープ、つまり自分自身のnamespaceの中に持ちます。
これが、scoped enumerationsと呼ばれる理由です。
scopedという名前があるなら、_unscoped_な列挙型もあるのではと思ったかもしれません。そのとおりです。
Unscoped enumerationsは、すべてが同じグローバル名前空間を共有するため、あまり使われなくなってきています。
この共有のせいで、上の例の_plastic_のように同じenumeratorsを持つ2つのunscoped enumerationsを定義することはできません。
また、unscoped enumerationsは暗黙的に整数に変換されます。
意外な結果については、以下の例を見てください。
enum CitrusFruits {
lemons, // 0
oranges, //1
};
enum IceCream {
walnut, // 0
apples, // 1
};
bool comparison{apples == oranges};
// => true
// Example from above:
bool comparison{DeckMaterial::plastic == WheelMaterial::plastic};
// => Does not compile!
scoped enumerationsを整数に変換したい場合は、static_cast<int>を使います。