我一直在想这个问题——为什么不能在switch语句的case标签后面声明变量呢?在c++中,你可以在任何地方声明变量(并且在第一次使用时声明它们显然是一件好事),但以下仍然不能工作:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
上面给了我以下错误(MSC):
'newVal'的初始化被'case'标签跳过
这在其他语言中似乎也是一个限制。为什么这是一个问题?
c++标准有:
可以将其转移到块中,但不能绕过带有初始化的声明。如果一个程序从一个具有自动存储持续时间的局部变量不在作用域中的点跳转到它在作用域中的点,那么该程序就是病态形式的,除非该变量具有POD类型(3.9),并且声明时没有初始化式(8.5)。
说明此规则的代码:
#include <iostream>
using namespace std;
class X {
public:
X()
{
cout << "constructor" << endl;
}
~X()
{
cout << "destructor" << endl;
}
};
template <class type>
void ill_formed()
{
goto lx;
ly:
type a;
lx:
goto ly;
}
template <class type>
void ok()
{
ly:
type a;
lx:
goto ly;
}
void test_class()
{
ok<X>();
// compile error
ill_formed<X>();
}
void test_scalar()
{
ok<int>();
ill_formed<int>();
}
int main(int argc, const char *argv[])
{
return 0;
}
显示初始化器效果的代码:
#include <iostream>
using namespace std;
int test1()
{
int i = 0;
// There jumps fo "case 1" and "case 2"
switch(i) {
case 1:
// Compile error because of the initializer
int r = 1;
break;
case 2:
break;
};
}
void test2()
{
int i = 2;
switch(i) {
case 1:
int r;
r= 1;
break;
case 2:
cout << "r: " << r << endl;
break;
};
}
int main(int argc, const char *argv[])
{
test1();
test2();
return 0;
}
这个问题的答案是我写的。然而,当我完成它,我发现答案已经关闭。所以我把它贴在这里,也许喜欢参考标准的人会发现它很有用。
问题的原始代码:
int i;
i = 2;
switch(i)
{
case 1:
int k;
break;
case 2:
k = 1;
cout<<k<<endl;
break;
}
实际上有两个问题:
1. 为什么我可以在case标签后声明一个变量?
这是因为在c++中标签必须是这样的:
N3337 6.1/1
标记语句:
…
属性说明符-seqopt case常量表达式:语句
…
在c++中声明语句也被认为是语句(与C相反):
N3337 6/1:
声明:
...
说明语句
...
2. 为什么我可以跳过变量声明,然后使用它?
因为:
N3337 6.7 / 3
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A
program that jumps
(The transfer from the condition of a switch statement to a case label is considered a jump in this respect.)
from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default
constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the
preceding types and is declared without an initializer (8.5).
因为k是标量类型,并且在声明时没有初始化,跳过它的声明是可能的。这在语义上是等价的:
goto label;
int x;
label:
cout << x << endl;
然而,如果x在声明点初始化,这将是不可能的:
goto label;
int x = 58; //error, jumping over declaration with initialization
label:
cout << x << endl;