enum VS enum class
最近見たコードでenum
の代わりに C++11 で導入されたenum class
が使われているのを見て嬉しかったです。
enum
とenum class
を比較すると、enum class
の方がstrong type
、つまり型のチェックが厳しいため、もっと安全です。
enum
enum OS
{
Windows = 0,
Linux = 1,
Mac = 2,
};
enum
はint
型をベースにしていて、int
に暗黙的に変換されるため、そのまま数字として使えます。例えば配列のインデックスとして使えますし、for ループでも回せます。とにかく使いやすいですが、悪く言えば、ただの特定の名前が付いているint
型の変数なのです。
例えば、上記のOS
というenum
型を期待している関数に全然違うenum
型の変数を渡しても、コンパイルエラーにならないです。
enum OS
{
Windows = 0,
Linux = 1,
Mac = 2,
};
enum Office
{
Office365 = 0,
OpenOffice = 1,
LibreOffice = 2,
};
enum OS x = Windows;
x = OpenOffice; // コンパイルは通ってしまう
enum class
enum class OS : short
{
Windows = 0,
Linux = 1,
Mac,
};
enum class
はベース型を定義できて、ベース型には明示的にしか変換できないです。上記の例で、OS
型を期待している関数に違うenum class
型の変数を渡すと、ちゃんとコンパイルエラーになります。
enum class OS : short
{
Windows = 0,
Linux = 1,
Mac,
};
enum class Office : short
{
Office365 = 0,
OpenOffice = 1,
LibreOffice,
};
OS os = OS::Windows;
os = Office::Office365; // コンパイルエラー!
でも型のチェックに厳しいからこそ、enum class
は正直使いづらいです。
例えば、enum class
型の変数を出力したい時は、毎回明示的にキャストしないとできないです。
でもキャストする時に注意してください。上記のenum class
の場合は、もちろん以下のように直接short
にキャストできます。
const OS platform = OS::Windows;
std::cout << "platform: " << static_cast<short>(platform) << std::endl;
でもこうすると、OS
のベース型を変えると、キャストしている箇所も全部一緒に変えないといけないため、本当は以下のようにキャストした方がいいです。
#include <type_traits>
std::cout << "platform: " << static_cast<std::underlying_type<OS>::type>(platform) << std::endl;
でもこれだと長くて読みづらいため、ユーティリティ関数を作るか以下のように関数テンプレートを作っておくといいです。
#include <type_traits>
template <typename Enumeration>
auto to_number(Enumeration const value) -> typename std::underlying_type<Enumeration>::type
{
return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}
以下のように使えます。
std::cout << "platform: " << to_number<OS>(platform) << std::endl;
でも本来は C++ 標準ライブラリがこういう関数を提供するべきです。と思ったら、C++23 で以下の関数が追加されるようですね。
#include <utility>
std::cout << "platform: " << std::to_underlying(platform) << std::endl;
これだったら、もう少し使いやすくなりますね。でも C++23 か… かなり先の話ですね…
後、注意ですが、せっかくenum class
を使っているのに、毎回ベース型にキャストしていると、ただのenum
に成り下がってしまいます。そういう使い方が必要でしたら、enum class
を使う意味がなくなってしまうでしょう。場合によって、enum
を使う方が良かったりします。
Discussion