Pynote

Python、機械学習、画像処理について

C++ - struct enum イディオム

概要

C++ のテクニック struct enum イディオムについて紹介する。

struct enum イディオムとは

列挙型 (C++11 以前からあった unscoped enum) を定義した場合、その列挙子は定義した名前空間に置かれる。
そのため、同じ名前の列挙子が同一の名前空間に存在する場合、名前が衝突してコンパイルエラーとなる。

enum Fruits
{
    Unknown,
    Banana,
    Apple,
    Orange,
};

enum Vegetable
{
    Unknown,  // C2365: 'Unknown': 再定義; 以前の定義は '列挙子' でした。
    Carrot,
    Onion,
    GreenPepper,
};

下記のようにすると、各列挙子はそれぞれ Fruits、Vegetable という名前空間に置かれる。つまり、さきほど名前が衝突していた2つの Unknown は Fruits::Unknown と Vegetable::Unknown と区別できるようになり、衝突しなくなる。

struct Fruits
{
    enum Type
    {
        Unknown,
        Banana,
        Apple,
        Orange,
    };
};

struct Vegetable
{
    enum Type
    {
        Unknown,
        Carrot,
        Onion,
        GreenPepper,
    };
};

スコープ付きで列挙子を参照するので、わかりやすいという利点もある。

Fruits::Banana
Vegetable::Carrot

scoped enum

同じ目的では C++11 で導入された scoped enum がある。

enum class Fruits
{
    Unknown,
    Banana,
    Apple,
    Orange,
};

enum class Vegetable
{
    Unknown,
    Carrot,
    Onion,
    GreenPepper,
};

struct enum イディオムとの違いは列挙型を int 型に変換する際に明示的にキャストを行う必要があるかということがある。

struct enum イディオムの場合

// struct enum -> int
int no = Fruits::Banana;  // 暗黙的に変換できる
// struct enum <- int
Fruits::Type favorite =  static_cast<Fruits::Type>(1);

scoped enum の場合

// scoped enum -> int
int no = static_cast<int>(Fruits::Banana);
// scoped enum <- int
Fruits favorite = static_cast<Fruits>(1);