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

时间:2013-11-17 13:23:15

标签: c++ switch-statement

{声明中使用}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;
}

6 个答案:

答案 0 :(得分:194)

{}表示范围的新块。

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

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

您将收到编译器错误,因为范围内已定义x

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

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

答案 1 :(得分:23)

TL; DR

case 中使用初个化程序或某些非平凡对象声明变量的唯一方法是使用{}或<{1}}引入块作用域其他具有自己范围的控制结构,如循环 if语句

血腥细节

我们可以看到个案只是标记语句,就像 goto 语句中使用的标签一样(< em>这已在C++ draft standard部分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 ;
}

完全有效( live example )。当然,如果你想在每个 case 中声明相同的变量,那么它们每个都需要它们自己的范围,但它在 switch 语句之外的工作方式也是一样的,所以不应该是一个大惊喜。

至于不允许跳过初始化的基本原理,defect report 467虽然涵盖了稍微不同的问题,但为自动变量提供了合理的理由:

  

[...]自动变量,如果没有显式初始化,可以有不确定的(“垃圾”)值,包括陷阱表示,[...]

查看在开关中扩展范围的情况可能更有趣的是多个案例最有名的例子可能是{{3}看起来像这样:

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
        }
}

答案 2 :(得分:6)

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

答案 3 :(得分: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";
}

答案 4 :(得分:1)

在开关中使用括号表示Rotem所说的新范围。

但是当你阅读时它也可以清晰明了。要知道案件在哪里停止,因为你可能有条件中断。

答案 5 :(得分:0)

原因可能是:

  1. 可读性,它在视觉上将每个案例增强为范围部分。
  2. 为多个交换机案例声明了具有相同名称的不同变量。
  3. 微优化 - 一个非常昂贵的资源分配变量的范围,您希望在离开案例范围时立即销毁,或者甚至是“GOTO”命令用法的更意大利面条场景。