1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

switchとブロック文

Last updated at Posted at 2022-12-10

はじめに

突然ですが、以下のコードのどこに誤りがあるでしょうか?

switch.cpp
#include <iostream>
using namespace std;

int main() {
    bool f = somefunc();
    switch (f) {
    case true:
        int res = somefunc1();
        cout << res << endl;
        break;
    case false:
        float res = somefunc2();
        cout << res << endl;
        break;
    }
}
答え

プログラムを実行すると以下のようなエラーが出ます。

switch.cpp: In function 'int main()':
switch.cpp:10:10: error: jump to case label
   10 |     case false:
      |          ^~~~~
switch.cpp:8:13: note:   crosses initialization of 'int res'
    8 |         int res = somefunc1();
      |             ^~~
switch.cpp:11:15: error: conflicting declaration 'float res'
   11 |         float res = somefunc2();
      |               ^~~
switch.cpp:8:13: note: previous declaration as 'int res'
    8 |         int res = somefunc1();
      |             ^~~

簡単に言うと、変数resが重複して宣言されています、ということですね。

なぜこんなことになるのか

C++のswitch文の仕様として、breakしない限りマッチしたcaseの後続のcaseブロックまでも実行される、というものがあります。これは、「フォールスルー(fall through)」と呼ばれるものです。

以下のプログラムをご覧ください。

badswitch.cpp
#include <iostream>
using namespace std;

int num = 0;

int main() {
    switch (num) {
    case 0:
        cout << "0" << endl;
    case 1:
        cout << "1" << endl;
    case 2:
        cout << "2" << endl;
    default:
        cout << "default" << endl;
    }
}

このプログラムを実行すると、

$ ./badswitch
0
1
2
default

case 0以下の全処理が実行されてしまいました。意図して書く場合は便利なものではありますが、バグの温床になることの方が多い気がします。

さて、本題に戻ると、フォールスルーという仕様があって複数のcaseブロックが実行される可能性があるために、caseの中で重複して変数を宣言することができないわけです。res、tmpなどの一時的にしか使わない変数名に対してres_intなどとするのは馬鹿馬鹿しいですね。

解決策

プログラム

switch.cpp
#include <iostream>
using namespace std;

int main() {
    bool f = somefunc();
    switch (f) {
        case true: {
            int res = somefunc1();
            cout << res << endl;
            break;
        } case false: {
            float res = somefunc2();
            cout << res << endl;
            break;
        }
    }
}

各caseブロックを、{}で挟んでしまえば解決です。

余談

"{"や"}"が縦に2つ並ぶのは気持ち悪いのでcaseブロックをインデントしましたが、こうすると全体で2インデントされることになって無駄に深くなってしまうのが難点ですね。

ブロック文のお話

実は、C++では、{}で挟むことで、どこにでも新たなスコープを作り出すことができます。これは「ブロック文」と呼ばれます。

sample.cpp
int main() {
    int x;
    {
        int tmp = somefunc1();
        x = somefunc2(tmp);
    }
    float y;
    {
        float tmp = somefunc3();
        y = somefunc4(tmp);
    }
}

こんなことだってできます。
これにより、

  • 変数の使いまわしができるようになる
  • 変数の有効範囲が制限されて可読性が上がる

等、複数のメリットがあります。

先ほどのswitch文では、これを適用したに過ぎないわけです。

まとめ

ブロック文は便利!!

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?