Menu
Coddy logo textTech

Enumクラスと強い型付け

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 80/104。

C++11では、従来のCスタイルの列挙型の欠点を解消するために、enum class(スコープを持つ列挙型とも呼ばれます)が導入されました。これらは、より強力な型安全性を提供し、列挙値の誤用を防ぎます。

従来の列挙型(enum)には問題があります。値が周囲のスコープに漏れ出し、整数に暗黙的に変換されるため、捉えにくいバグの原因となります:

// 従来の列挙型 - 問題あり
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };  // エラー!RedとGreenは既に定義されています

int x = Red;  // 暗黙的にint型(0)に変換される
if (Red == 0) { }  // コンパイル可能 - 列挙型とint型を比較している

列挙型クラス(Enum classes)は、値をスコープ内に限定し、暗黙的な型変換を防ぐことで、これらの問題を解決します。

enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };  // OK - 衝突しません

Color c = Color::Red;      // スコープ演算子を使用する必要があります
// int x = c;              // エラー!暗黙的な型変換はありません
int x = static_cast<int>(c);  // 明示的な型変換が必要です

// if (c == 0) { }         // エラー!int型と比較できません
if (c == Color::Red) { }   // OK - 同じ型同士の比較

メモリ制御のために、基底となる型を指定することもできます:

enum class Status : uint8_t {
    Inactive = 0,
    Active = 1,
    Pending = 2
};

EnumクラスはOOP設計と親和性が高く、コードの可読性を向上させ、実行時ではなくコンパイル時に型エラーを検出できるようにします。

challenge icon

チャレンジ

簡単

列挙型クラス(enum classes)を使用して、異なる優先度レベルとタスクステータスを表現するタスク優先度システムを構築しましょう。これにより、列挙型クラスがどのように型安全性を提供し、従来の列挙型で発生しがちなバグを防ぐかを確認できます。

コードは以下の3つのファイルに分けて構成します:

  • TaskTypes.h: タスクシステム用の列挙型クラスを定義します。

    Priority 列挙型クラスを作成し、値として LowMediumHighCritical を持たせます。基底型を int と指定し、それぞれの値を 1、2、3、4 とします。

    Status 列挙型クラスを作成し、値として PendingInProgressCompleted を持たせます。

    2つのヘルパー関数を用意します:

    • priorityToString(Priority p) — 優先度を文字列("Low"、"Medium"、"High"、または "Critical")として返します。
    • statusToString(Status s) — ステータスを文字列("Pending"、"InProgress"、または "Completed")として返します。
  • Task.h: 定義した列挙型クラスを使用する Task クラスを定義します。

    Task クラスには、名前(string)、Priority、および Status を保持させます。コンストラクタは名前と優先度を受け取り、ステータスのデフォルト値は Status::Pending とします。

    以下のメソッドを実装してください:

    • setStatus(Status s) — タスクのステータスを更新します。
    • getPriorityValue()static_cast を使用して、優先度の基底となる整数値を返します。
    • display() — タスクを次の形式で出力します:[name] - Priority: [priority] (Value: [value]) - Status: [status]
  • main.cpp: 3つの入力を読み込みます:
    1. タスク名(文字列)
    2. 整数としての優先度レベル(1=Low, 2=Medium, 3=High, 4=Critical)
    3. 整数としてのステータス(0=Pending, 1=InProgress, 2=Completed)

    指定された名前と優先度で Task を作成します(static_cast を使用して整数を適切な Priority 列挙値に変換してください)。次に、3番目の入力に基づいてステータスを設定します。最後に、display() を呼び出してタスクの詳細を表示します。

例えば、入力が Fix bug31 の場合:

Fix bug - Priority: High (Value: 3) - Status: InProgress

入力が Write docs12 の場合:

Write docs - Priority: Low (Value: 1) - Status: Completed

入力が Deploy app40 の場合:

Deploy app - Priority: Critical (Value: 4) - Status: Pending

列挙型クラスによって、優先度とステータスの値が完全に分離されていることに注目してください。誤って PriorityStatus を比較したり、一方を他方に代入したりすることはできません。整数と列挙値の間の変換に明示的な static_cast が必要になることで、コードの意図が明確になり、潜在的なバグを防ぐことができます。

チートシート

C++11では、従来のCスタイルの列挙型(enum)よりも強力な型安全性を提供するために、enum class(スコープを持つ列挙型)が導入されました。

従来の列挙型には、スコープの汚染や暗黙的な型変換といった問題があります。

// 従来の列挙型 - 問題あり
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };  // エラー!RedとGreenは既に定義されています

int x = Red;  // int型に暗黙的に変換される
if (Red == 0) { }  // コンパイル可能 - 列挙型とint型を比較している

enum classは、スコープ解決を必須とし、暗黙的な型変換を防止することで、これらの問題を解決します。

enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };  // OK - 衝突しない

Color c = Color::Red;      // スコープ演算子を使用する必要がある
// int x = c;              // エラー!暗黙的な型変換は行われない
int x = static_cast<int>(c);  // 明示的な型変換が必要

// if (c == 0) { }         // エラー!int型と比較できない
if (c == Color::Red) { }   // OK - 同じ型同士を比較している

メモリ制御のために、基底となる型を指定することができます:

enum class Status : uint8_t {
    Inactive = 0,
    Active = 1,
    Pending = 2
};

enum classと整数の間で変換を行うには、明示的なキャストを使用します:

// 整数からenum classへ
Priority p = static_cast<Priority>(3);

// enum classから整数へ
int value = static_cast<int>(p);

自分で試してみよう

#include <iostream>
#include <string>
#include "Task.h"

using namespace std;

int main() {
    // 入力を読み込む
    string taskName;
    int priorityInt;
    int statusInt;
    
    getline(cin, taskName);
    cin >> priorityInt;
    cin >> statusInt;
    
    // TODO: 指定された名前と優先度で Task を作成する
    // static_cast を使用して priorityInt を Priority 列挙型に変換する
    
    // TODO: statusInt に基づいてタスクのステータスを設定する
    // static_cast を使用して statusInt を Status 列挙型に変換する
    
    // TODO: display() を呼び出してタスクの詳細を表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン