在case语句中使用{}.为什么?

mah*_*ood 100 c++ switch-statement

什么是使用点{}在一个case声明?通常,无论case语句中有多少行,都会执行所有行.这只是关于旧/新编译器的规则还是有背后的东西?

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

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

Rot*_*tem 193

{}表示的一个新的块范围.

考虑以下非常人为的例子:

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

您将收到编译器错误,因为x已在作用域中定义.

将它们分离到它们自己的子范围将消除x在switch语句之外声明的需要.

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

  • 实际上,即使你跳过变量x的第二个声明,IMO也会遇到编译器错误. (11认同)
  • 标准的第6.7(3)节(2005年草案的编号)规定您不能跳过初始化,因此您无法在案例块中进行初始化. (6认同)
  • @MatthieuM.我知道MS Visual Studio 2010将具有Abhishek指示的行为这一事实:它不会在案例中编译任何变量声明(除非您使用大括号来表示该案例中的新范围).这是否符合标准,我不知道. (2认同)

Sha*_*our 23

TL; DR

在一个case中声明一个带有初始化器或一些非平凡对象的变量的唯一方法是引入一个块作用域,{}或者使用其自身作用域的其他控制结构,如loopif语句.

血淋淋的细节

我们可以看到案例只是标记语句,如与goto语句一起使用的标签(这在C++草案标准第6.1节标记语句中有所涉及),我们可以从第3节中看到,在许多情况下不允许跳过声明,包括初始化的那些:6.7

可以转换为块,但不能以初始化绕过声明的方式.从具有自动存储持续时间的变量不在范围内的点跳到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
}
Run Code Online (Sandbox Code Playgroud)

注意,这里有一些细微之处,你可以跳过没有初始化的标量声明,例如:

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}
Run Code Online (Sandbox Code Playgroud)

是完全有效的(实例).当然,如果你想在每种情况下声明相同的变量,那么它们每个都需要它们自己的范围,但它在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
        }
}
Run Code Online (Sandbox Code Playgroud)


Yak*_*ont 6

这是一种习惯,允许您将变量声明与生成的析构函数(或作用域冲突)一起注入case子句中.另一种看待它的方式是他们正在为他们希望的语言编写,其中所有流控制都由块而不是语句序列组成.