caseステートメントで{}を使用します。どうして?


101

使用してのポイントは何である{}におけるcase文は?通常、caseステートメントの行数に関係なく、すべての行が実行されます。これは古い/新しいコンパイラに関するルールですか、それとも背後に何かありますか?

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}

そして

int a = 0;
switch (a) {
  case 0:
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
}

57
1つの用途は、caseステートメントで宣言された変数のスコープを制限することです。
Abhishek Bansal 2013年


1
インデントも多すぎます。ケースは、switchステートメントのブロック内の単なるラベルです。これらは追加のネストを導入しないため、switchキーワードと一致する必要があり、2番目の例では、囲まれたステートメントは1回だけインデントされます。の後にいかに厄介な4つのスペースをインデント解除するかに注意してくださいbreak;
Kaz

ジャックのコメントが指摘し、いくつかの微妙な点を見落としているため、受け入れられた回答は部分的にしか正しくありません。
Shafik Yaghmour 2013年

参考までに:C ++ではなくC(C11でも)では、宣言にラベルを付けることはできません。それらは構文カテゴリには含まれませんstatement。C ++では、可能です(構文カテゴリの1つのコンポーネントはstatementですdeclaration statement)。
ジョナサンレフラー

回答:


195

{}新しいブロック表しスコープを

以下の非常に不自然な例を考えてみましょう。

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}

xは既にスコープで定義されているため、コンパイラエラーが発生します。

これらを独自のサブスコープに分離するとx、switchステートメントの外側で宣言する必要がなくなります。

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

11
実際にIMOでは、変数xの2番目の宣言をスキップしても、コンパイラエラーが発生します。
Abhishek Bansal 2013年

1
このスタイルを過度に使用し、switchブロック内に大きなブロックを配置すると、ケースに従うのが難しくなります。私はステートメントを小さく保つことを好みます。
masoud 2013年

2
@MatthieuM。MS Visual Studio 2010にはAbhishekが示す動作があることは知っています。ケース内の変数宣言はコンパイルされません(中括弧を使用してそのケース内の新しいスコープを示す場合を除く)。それが基準に合っているかどうかはわかりません。
KRyan 2013年

1
@KRyan:そうではありませんが、これは非常に安全な代替手段であり、私がこれを強制したことについてほとんど非難することはできません。
Matthieu M.

6
標準(2005ドラフトの番号)のセクション6.7(3)は、初期化をジャンプできないことを指定しているため、ケースブロックで初期化を行うことはできません。
Jack Aidley、2013年

23

TL; DR

ケース内で初期化子または重要なオブジェクトを使用して変数を宣言できる唯一の方法は、ループまたはifステートメントのような独自のスコープを持つブロックスコープを使用する{}か、他の制御構造を導入することです。

ゴリー詳細

我々はそれを見ることができるケースがちょうどされている文ラベルのようなラベルで使用するのgoto文を(これはで覆われているC ++のドラフト標準のセクション6.1ラベル付きステートメント)、我々はセクションから見ることができます6.7段落3ジャンプが宣言を渡すことを、多くの場合、許可されていません、初期化付きのものを含む:

ブロックに転送することは可能ですが、初期化で宣言をバイパスする方法ではできません。自動ストレージ期間を持つ変数がスコープ内にないポイントからスコープ内にあるポイントに87をジャンプするプログラムは、変数がスカラー型、自明なデフォルトコンストラクターおよび自明なデストラクタを持つクラス型でない限り、形式が正しくありません。これらのタイプのいずれかのcv修飾バージョン、または前述のタイプのいずれかの配列であり、初期化子なしで宣言されています(8.5)。

この例を提供します:

void f() {
 // ...
 goto lx; // ill-formed: jump into scope of a

 ly:
  X a = 1;
 // ...
 lx:
  goto ly; // OK, jump implies destructor
          // call for a followed by construction
          // again immediately following label ly
}

ここにいくつかの微妙な点があることに注意してください。たとえば、初期化されていないスカラー宣言を飛び越すことができます。次に例を示します。

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}

完全に有効です(実例)。もちろん、それぞれの場合に同じ変数を宣言したい場合は、それぞれに独自のスコープが必要ですが、switchステートメント以外でも同じように機能するため、大きな驚きではありません。

初期化のジャンプを許可しない理由については、欠陥レポート467が少し異なる問題をカバーしていますが、自動変数の妥当なケースを提供します

[...]自動変数は、明示的に初期化されていない場合、トラップ表現を含む不確定な(「ガベージ」)値を持つ可能性があります[...]

スイッチ内のスコープを複数のケースに拡張する場合を見ると、おそらくもっと興味深いでしょう。この最も有名な例は、おそらく次のようなDuffのデバイスです。

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            case 7:         *to = *from++;
            case 6:         *to = *from++;
            case 5:         *to = *from++;
            case 4:         *to = *from++;
            case 3:         *to = *from++;
            case 2:         *to = *from++;
            case 1:         *to = *from++;
                        } while(--n > 0);    // <- Scope end
        }
}

6

これは、結果のデストラクタ(またはスコープの競合)を含む変数宣言をcase句に挿入できるようにする習慣です。別の見方をすると、彼らは彼らが望んでいた言語のために書いているのです。そこでは、すべてのフロー制御はステートメントのシーケンスではなくブロックで構成されています。


4

これを基本的なコンパイラの制限にチェックすると、何が起こっているのか不思議に思うでしょう:

int c;
c=1;

switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}

    default : cout<<"def";
}

これはあなたにエラーを与えます:

error: jump to case label [-fpermissive]
error:   crosses initialization of int* i

これはしませんが:

int c;
c=1;

switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }

    default : cout<<"def";
}

1

スイッチでブラケットを使用することは、Rotemによると、スコープの新しいブロックを示します。

ただし、読みやすくするためにも同じように使用できます。条件付きブレークが発生する可能性があるため、ケースがどこで停止するかを知る。


0

理由は次のとおりです。

  1. 読みやすさ、スコープ付きセクションとして各ケースを視覚的に強化します。
  2. 複数のスイッチケースに対して同じ名前の異なる変数を宣言します。
  3. マイクロ最適化-ケースのスコープを離れるとすぐに破棄する、非常に高価なリソース割り当て変数のスコープ、または「GOTO」コマンドの使用法のさらにスパゲッティシナリオ。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.