#include <iostream>
int main(){
int b = 2;
if(int a = 0){ // #condition
}else if(b == 2){
int a; //#1
}
}
上面的代码可以在 gcc 和 clang 中编译。但是,根据相关规则的说法,此代码应格式不正确。 规则是:
stmt.stmt#3
由条件中的声明引入的名称(由 decl-specifier-seq 或条件的声明符引入)从其声明点到条件控制的子语句的末尾都在范围内。如果在条件控制的子语句的最外层块中重新声明名称,则重新声明名称的声明格式不正确。
else
后面的 if 语句不是由条件控制的子语句吗?(即,只有#condition
执行的条件才会false
else
之后的 if 语句才会被执行)。那么,为什么在这种子语句的最外层块中重新声明名称的声明可以被视为格式正确的代码?
也许在版本n4659
的规则中,"由条件控制的子语句"这句话有一些论据,但是,这样的想法在最新的草案中显然是明确的。
stmt.stmt#stmt.pre-2
语句的子语句是以下语句之一:
对于选择语句,它的任何语句(但不是它的 init-statement)
这意味着else
后面的语句是主if-statement
的子语句,然后接下来的规则说: stmt.stmt#stmt.pre-5
或迭代语句中引入的名称从其声明点到语句子语句的末尾都在范围内。这样的名称不能在任何子语句的最外层块中重新声明
该规则显然表明,我们不能重新声明与在这些子语句的最外层块中声明的名称相同的名称。所以,我很好奇,是我误解了这些规则,还是草案中有一些缺陷?
No.
您缺少else
子语句引入的块范围:
[stmt.selected.general/2]
:选择语句中的子语句(每个子语句,以if
语句的else
形式)隐式定义一个块范围([basic.scope])。如果选择语句中的子语句是单个语句而不是复合语句,则就好像它被重写为包含原始子语句的复合语句一样。[..]
即你的代码实际上是:
#include <iostream>
int main()
{
int b = 2;
if (int a = 0) {
}
else {
if (b == 2) {
int a;
}
}
}
因此,您正在查看的块(由嵌套if
引入的块)不是有问题的"最外层"块。因此,尽管a
在该块的范围内,但它可以被阴影化。
这意味着你不能在"裸"else
中声明a
,即以下内容格式不正确:
#include <iostream>
int main()
{
int b = 2;
if (int a = 0) {
}
else {
int a;
}
}
/*
prog.cpp: In function ‘int main()’:
prog.cpp:9:9: error: redeclaration of ‘int a’
int a;
^
prog.cpp:6:11: note: ‘int a’ previously declared here
if (int a = 0) {
*/
stmt.stmt#stmt.pre-5 中的语句明确指出:
[注2:在任何子语句之外的选择语句或迭代语句中引入的名称从其声明点到语句子语句的末尾都在范围内。这样的名称不能在任何子语句的最外层块([basic.scope.block])中重新声明。— 尾注]
这里的关键术语是在stmt.block#1中定义的最外层块:
复合语句(也称为块)将一系列语句分组到单个语句中。
复合语句:
{ statement-seq opt }
。
复合语句定义块范围。
所以stmt.stmt#stmt.pre-5本质上是在说:
if (int a = 0)
{ // outermost block
int a; // so ill-formed
}
但
if (int a = 0)
{ // outermost block
{ // inner block
int a; // so well-formed
}
}
相同的规则适用于您的示例,其中包含嵌套if
语句引入的块。