switch は何のためにあるか
switch は 1 つの値を固定された選択肢のリストと比較し、一致した分岐を実行します。同じ変数を異なる定数と何度も比較する長い if/else if の連なりを書いていると気づいたら、switch ならその意図をより明確に表現でき、しばしばより高速なジャンプテーブルにコンパイルされます。
switch は整数に近い値に対して、コンパイル時定数との等価チェックしか行いません。範囲を調べたり、文字列を比較したり、条件を組み合わせたりはできません。そうしたものには if/else のままにしてください。
基本の switch
switch に値を渡し、気にかける値ごとに case ラベルを並べます。各分岐は break で終わります。
値 3 は case 3: に一致するので "Wednesday" が出力され、break が switch の外へ抜けます。default 分岐は受け皿で、どの case にも一致しないときに実行されます。これは省略可能ですが、すべての値が処理されると確信できない限りは入れておきましょう。
case ラベルは条件ではなく、コロンが続く裸の定数であることに注意してください。case 3: と書き、case day == 3: とは決して書きません。
break とフォールスルー
これは C++ における switch の最も重要な落とし穴です。ある case に一致した後、実行は次の case で止まりません。break か閉じ括弧に達するまでそのまま走り続けます。break 文がないとどうなるか見てみましょう。
"one" だけを期待するかもしれませんが、実際には 4 行すべてが出力されます。case 1: に一致するとそこから switch に入り、その下にあるすべてのラベルへとフォールスルーしていくのです。各分岐の後に break; を加えれば、望んだとおりの 1 行だけになります。break の書き忘れは「なぜ switch が余計なコードまで実行するのか?」というバグの典型的な原因です。
case をグループ化する意図的なフォールスルー
フォールスルーは常に間違いというわけではありません。複数の case に 1 つの本体を共有させる慣用的な方法でもあります。case を空のまま(文も break もなし)にしておくと、すべて次のブロックへ流れ込みます。
'A'、'B'、'C' はすべて同じ "Pass" の行にたどり着きます。最初の 2 つの case が空で、3 つめへフォールスルーするからです。これはきれいで意図どおりです。何らかのコードを実行した後で意図的にフォールスルーするときは、コメントで明記しましょう。あるいは C++17 以降では [[fallthrough]]; 属性を使うと、コンパイラに「はい、これは意図的です」と伝え、フォールスルーの警告を抑制できます。
enum に対する switch
switch は enum と自然に組み合わさります。enum はまさに「固定された値の集合のうちの 1 つ」だからです。さらにコンパイラは、列挙子のいずれかを処理し忘れていると警告してくれることがあります。
スコープ付きの enum class では、各ラベルを修飾する必要があります(Direction::East)。すべての列挙子を網羅しているので default は不要です。しかも多くのコンパイラは、後で 5 つめの方向を追加してその case を加え忘れると警告してくれます。このコンパイラの助けは、enum に対して if/else の連なりよりも switch を選ぶ大きな理由です。
落とし穴:case 内での宣言
ある case の中で初期化子付きの変数を宣言し、スコープを区切らずに他の case をまたいで見えるようにすることはできません。これはよくあるコンパイルエラーです。
switch (x) {
case 1:
int n = 10; // エラー: case 2 へのジャンプがこの初期化を飛び越す
cout << n;
break;
case 2:
cout << "two";
break;
}
コンパイラがこれを拒否するのは、case 2: へ落ち込むと、n がまだスコープ内にあるのに n の初期化を飛び越えてしまうからです。解決策は、case の本体を独自の波括弧で囲み、変数に専用のブロックを与えることです。
case が独自のローカル変数を必要とするときは、いつでも波括弧を付けましょう。これは変数が下の case へ漏れ出すのも防いでくれます。
次へ:for ループ
switch と if は、プログラムがどのコードを一度実行するかを選べるようにします。しかし多くの仕事は、同じことを何度も繰り返すこと、つまり数えたり、リストを反復処理したり、条件が変わるまで繰り返したりすることを意味します。for ループはそのための主力であり、それが次のページです。
よくある質問
C++ で if-else ではなく switch を使うべきなのはどんなときですか?
メニューの選択肢、曜日番号、enum のように、整数に近い 1 つの値を多数の固定された定数の選択肢と比較するときは switch を使いましょう。長い if/else if の連なりよりも読みやすく、コンパイラが分岐を最適化できます。条件が範囲(x > 10)、浮動小数点値、文字列、複数の変数に関わる場合は if/else のままにしてください。switch はそのいずれもできません。
C++ の switch 文ではなぜ break が必要なのですか?
ある case に一致すると、C++ は break か switch の末尾に達するまで、続く case を実行し続けます。これをフォールスルーと呼びます。break はそれを止めます。break の書き忘れは典型的なバグです。case 1: に一致したのに、うっかり case 2、case 3、default のコードまで実行してしまうのです。
C++ ではどんな型に対して switch できますか?
整数型または列挙型のみです。int、char、short、long、bool、スコープ付き/スコープなしの enum、そしてそれらのいずれかに変換できるものです。double、float、std::string に対しては switch できません。それらには if/else を使ってください。各 case ラベルはコンパイル時定数でなければなりません。