最終更新日時: 2024年12月22日 15時27分29秒 (JST)
Raclamusi が更新

履歴 編集

# スコープを持つ列挙型 [N2347](C++11)

このページはC++11に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

## 概要

`enum class`で定義した列挙型は、従来の`enum`に加えて、「整数型への暗黙の型変換を行わない」「列挙型のスコープを持つ」という機能を持つ。

    enum class Color { Red, Green, Blue };

    // 単にRedと指定するのではなく、どの列挙型に属するのかを指定する
    Color c = Color::Red;

    // 明示的な型変換は許可する
    int color = static_cast<int>(Color::Red);
    //int color = Color::Red; // コンパイルエラー : 暗黙の型変換はできない

## 仕様

- `enum class`もしくは`enum struct`で定義した列挙型は、「スコープを持つ列挙型 (scoped enumeration type)」という。`enum class`と`enum struct`に、機能の違いはない
- `enum`で定義した列挙型は、「スコープを持たない列挙型 (unscoped enumeration type)」という

- 列挙型には、型名の後ろにコロン `:` 区切りで、基底の整数型を指定できる。基底型として指定した整数型のCV修飾は無視される

      // 基底型をintとする
      enum class Color : int {
        Red, Green, Blue
      };

  - 列挙子の値が基底型の範囲に収まらない場合、プログラムは不適格となる
  - 列挙型の基底型を取得するには、`<type_traits>`ヘッダで定義される`std::underlying_type`を使用する

- 列挙型は、先行宣言でも基底型を指定できる。その場合、再宣言および定義でも、同じ基底型を明示的に指定する必要がある

- スコープを持つ列挙型に基底型を指定しない場合、基底型は`int`となる
- スコープを持たない列挙型に基底型を指定しない場合、その列挙型の全ての列挙子の値を表現できる整数型のいずれかが基底型となる（どの整数型となるかは実装定義だが、すべての列挙子の値が`int`もしくは`unsigned int`に収まる限り`int`より大きな型にはならない）
- スコープを持たない列挙型の列挙子が空である場合、値`0`ひとつを列挙子として持つ
- 2つの列挙型の基底型が同じである場合、それらの列挙型は互換性のあるメモリレイアウトを持つ
- 列挙子に値を指定しない場合、最初の列挙子には定数値`0`が設定される。2つ目以降の列挙子は、ひとつ前の値を`1`進めた値を持つ

### スコープを持つ列挙型に合わせた、enumの拡張

スコープを持つ列挙型の導入に合わせて、スコープを持たない列挙型もC++11で以下の機能拡張が行われた

- 基底型を指定できるようになった
- 基底型が指定されている場合は、先行宣言ができるようになった
- 列挙型名をスコープとして書けるようになった (`列挙型名::列挙子`が許可された)

詳細は、上述した仕様を参照。

## 例

    #include <cassert>
    #include <type_traits>

    enum class Color { Red, Green, Blue };
    enum class CharColor : char { Red, Green, Blue }; // 基底型をcharにする

    int main()
    {
      // 単にRedと指定するのではなく、どの列挙型に属するのかを指定する
      Color c = Color::Red;

      // 明示的な型変換は許可する
      int colorValue = static_cast<int>(Color::Red);
      //int colorValue = Color::Red; // コンパイルエラー : 暗黙の型変換はできない
      assert(colorValue == 0);

      // 列挙型の基底型を取得して、その型にキャスト
      auto colorValue2 = static_cast<std::underlying_type<Color>::type>(Color::Red);
      assert(colorValue2 == 0);
    }

### 出力

## この機能が必要になった背景・経緯

C++03は、C99の列挙型に対する改善は提供していたが、依然として以下のような問題が残っていた：

- 型の安全性
- 予期しないエラー(スコープがないことによる名前衝突、上記の型安全性の問題による予期しない型変換)
- コードのわかりやすさ(明確さ)
- コードの移植性

ECMA規格になっているC++/CLIが、現在のスコープを持つ列挙型と等価の機能を持っていたために、その経験を標準C++に取り入れることとなった。

## 関連項目

- `underlying_type`
- `to_underlying`
- enum class変数の初期値として整数を指定する際の規則を調整
- スコープ付き列挙型のusing宣言

## 参照

- N1513 Improving Enumeration Types
- N1579 Strongly Typed Enums
- N1719 Strongly Typed Enums (revision 1)
- N2213 Strongly Typed Enums (revision 2)
- N2347 Strongly Typed Enums (revision 3)
- N2499 Forward declaration of enumerations