在子语句的最外层块中重新声明的变量


#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执行的条件才会falseelse之后的 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语句引入的块。

相关内容

  • 没有找到相关文章

最新更新