TL; DR
可以在实例内部使用初始化器或一些非平凡的对象声明变量的唯一方法是使用或具有其自身范围(如循环或if语句)的其他控制结构引入块范围。{}
血腥细节
我们可以看到案例只是带有标签的语句,例如与goto语句一起使用的标签(在C ++草案标准第6.1节Labeled语句中对此进行了介绍),并且从第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
}
注意,这里有一些微妙之处,您可以跳过没有初始化的标量声明,例如:
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
}
}