フリーキーズ | 独学プログラミング

【C++】enum classの使い方を初心者向けに解説!従来のenumより安全な型付け列挙型を理解する

リンドくん

リンドくん

先生、C++でenum classっていうのがあるって聞いたんですけど、普通のenumと何が違うんですか?

たなべ

たなべ

enum classはC++11で導入された、より安全で使いやすい列挙型なんだ。 従来のenumよりも型安全性が高くて、現代のC++では積極的に使うべき機能の一つなんだよ。

プログラミングでは、定数を管理する際に「列挙型(enum)」という便利な機能があります。
しかし、従来のC++のenumには、意図しない型変換やスコープの問題など、いくつかの課題がありました。

そこで登場したのがenum classです。
これは「強く型付けされた列挙型」とも呼ばれ、従来のenumの問題点を解決し、より安全で読みやすいコードを書けるようになっています。

この記事では、enum classの基本概念から実際の使い方まで、プログラミング初心者の方でも理解できるよう、丁寧に解説していきます。

プログラミング学習でお悩みの方へ

HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。

✓ 質問し放題

✓ β版公開中(2025年内の特別割引)

HackATAの詳細を見る

enum classとは?従来のenumとの違いを理解しよう

リンドくん

リンドくん

そもそも列挙型って何のために使うんですか?

たなべ

たなべ

列挙型は関連する定数をグループ化して管理するためのものなんだ。
例えば、ゲームのプレイヤーの状態を表現する時に「待機」「移動」「攻撃」みたいな値を定義するのに便利なんだよ。

列挙型の基本的な役割

列挙型は、関連する定数をまとめて定義するための仕組みです。
信号機の色を表現する例を考えてみましょう。

// 従来のenumの例
enum TrafficLight 
{
    RED,
    YELLOW,
    GREEN
};

このように、関連する値を一つのグループとして定義することで、コードの可読性が向上し、間違いを防ぐことができます。

従来のenumの問題点

しかし、従来のenumにはいくつかの問題がありました。

1. 暗黙的な型変換が発生する

enum TrafficLight { RED, YELLOW, GREEN };

int main() 
{
    TrafficLight light = RED;
    int number = light;  // 暗黙的にintに変換される(問題!)
    
    if (light == 0)      // intと比較できてしまう(問題!)
    {
        // 処理
    }
    
    return 0;
}

2. スコープ汚染が起こる

enum Color { RED, GREEN, BLUE };
enum TrafficLight { RED, YELLOW, GREEN };  // エラー!REDとGREENが重複

enum classによる解決

これらの問題を解決するのがenum classです。

enum class TrafficLight 
{
    Red,
    Yellow,
    Green
};

enum class Color 
{
    Red,
    Green,
    Blue
};

int main() 
{
    TrafficLight light = TrafficLight::Red;
    Color color = Color::Red;  // 問題なし!異なる型として扱われる
    
    // int number = light;     // エラー!暗黙的変換は発生しない
    // if (light == 0) {}      // エラー!異なる型との比較はできない
    
    return 0;
}

enum classを使うことで、型安全性が大幅に向上し、予期しないバグを防ぐことができます。

enum classの基本的な使い方とサンプルコード

リンドくん

リンドくん

実際にenum classを使うときは、どんな風に書けばいいんですか?

たなべ

たなべ

基本的な書き方は従来のenumとほとんど同じなんだ。
ただし、値にアクセスする時はスコープ解決演算子(::)を使う必要があるよ。

基本的な定義方法

enum classの基本的な構文は以下のようになります。

enum class 列挙型名 
{
    値1,
    値2,
    値3
};

サンプルコード1 ゲームキャラクターの状態管理

#include <iostream>

enum class PlayerState 
{
    Idle,
    Walking,
    Running,
    Attacking,
    Defending
};

void handlePlayerAction(PlayerState state) 
{
    switch (state) 
    {
    case PlayerState::Idle:
        std::cout << "プレイヤーは待機中です\n";
        break;
    case PlayerState::Walking:
        std::cout << "プレイヤーは歩いています\n";
        break;
    case PlayerState::Running:
        std::cout << "プレイヤーは走っています\n";
        break;
    case PlayerState::Attacking:
        std::cout << "プレイヤーは攻撃中です\n";
        break;
    case PlayerState::Defending:
        std::cout << "プレイヤーは防御中です\n";
        break;
    }
}

int main() 
{
    PlayerState currentState = PlayerState::Walking;
    handlePlayerAction(currentState);
    
    return 0;
}

サンプルコード2 基底型の指定

enum classでは、基底型(underlying type)を明示的に指定することもできます。

#include <iostream>
#include <cstdint>

enum class Priority : std::uint8_t 
{
    Low = 1,
    Medium = 2,
    High = 3,
    Critical = 4
};

enum class ErrorCode : int 
{
    Success = 0,
    InvalidInput = -1,
    FileNotFound = -2,
    NetworkError = -3
};

int main() 
{
    Priority taskPriority = Priority::High;
    ErrorCode result = ErrorCode::Success;
    
    // 明示的なキャストで基底値を取得
    std::cout << "優先度: " << static_cast<int>(taskPriority) << std::endl;
    std::cout << "エラーコード: " << static_cast<int>(result) << std::endl;
    
    return 0;
}

enum classの値の比較

enum classは同じ型同士でのみ比較が可能です。

enum class Size 
{
    Small,
    Medium,
    Large
};

int main() 
{
    Size mySize = Size::Medium;
    Size otherSize = Size::Large;
    
    if (mySize == Size::Medium) 
    {
        std::cout << "サイズはMediumです\n";
    }
    
    if (mySize != otherSize) 
    {
        std::cout << "サイズが異なります\n";
    }
    
    return 0;
}

enum classを使う際のベストプラクティス

名前付けの規則

enum classを使用する際は、一貫した命名規則を採用することが重要です。

// 推奨される命名規則
enum class ConnectionState 
{
    Disconnected,    // 各値はPascalCase
    Connecting,
    Connected,
    Error
};

enum class LogLevel 
{
    Debug,
    Info,
    Warning,
    Error,
    Critical
};

switch文での完全性チェック

コンパイラの警告を活用して、すべてのenum値を処理しているかチェックしましょう。

std::string getLogLevelName(LogLevel level) 
{
    switch (level) 
    {
    case LogLevel::Debug:   return "DEBUG";
    case LogLevel::Info:    return "INFO";
    case LogLevel::Warning: return "WARNING";
    case LogLevel::Error:   return "ERROR";
    case LogLevel::Critical: return "CRITICAL";
    // defaultを書かないことで、新しい値が追加された時にコンパイラが警告を出す
    }
    return "UNKNOWN";  // 到達しないが、警告を避けるため
}

型安全性を最大限活用する

enum classの最大の利点である型安全性を活用しましょう。

enum class Temperature 
{
    Cold,
    Warm,
    Hot
};

enum class Speed 
{
    Slow,
    Medium,
    Fast
};

// 間違った型の組み合わせを防ぐ
void setAirConditioner(Temperature temp, Speed fanSpeed) 
{
    // 実装
}

int main() 
{
    setAirConditioner(Temperature::Cold, Speed::Fast);  // OK
    // setAirConditioner(Speed::Fast, Temperature::Cold);  // コンパイルエラー!
    
    return 0;
}

まとめ

リンドくん

リンドくん

enum classってこんなに便利だったんですね!今まで知らなかったので、これからは積極的に使ってみます。

たなべ

たなべ

そうだね!enum classはモダンなC++プログラミングの基本とも言える機能なんだ。
型安全性が向上して、バグの少ないコードが書けるようになるから、ぜひマスターしてほしいな。

この記事では、C++のenum classについて基本概念から実践的な活用例まで詳しく解説してきました。

enum classの主な利点をおさらいしましょう。

  • 型安全性の向上 - 意図しない型変換や比較を防ぐ
  • スコープの明確化 - 名前の衝突を避けられる
  • コードの可読性向上 - 意図が明確に表現される
  • 保守性の向上 - 変更に強い設計が可能

enum classは、単なる定数の集まりではありません。プログラムの状態や分類を型安全に表現するための強力な機能です。
特に、複雑なアプリケーション開発では、その恩恵を強く実感できるでしょう。

今後C++でプログラミングをする際は、従来のenumではなく、ぜひenum classを積極的に活用してみてください。
最初は少し記述が増えるように感じるかもしれませんが、その分得られる安全性と保守性は非常に価値の高いものです。

この記事をシェア

関連するコンテンツ