6.4选择语句 [stmt.select]
在条件中声明的名称(由条件的type-specifier-seq
或声明符引入)从其声明点起到受控制子语句结束时处于作用域内。如果该名称在受控制子语句的最外层块中被重新声明,则重新声明该名称的声明是非法的。 [例子:
if (int x = f()) {
int x; // ill-formed, redeclaration of x
}
else {
int x; // ill-formed, redeclaration of x
}
——示例结束]
(着重标记为我的部分)
这基本意味着 i
的作用域从条件开始,在 if
块结束之后才结束,在此期间,else
块也是 if
块的一部分。
你对嵌套 if
的第二个问题基于(错误的)假设,即 else-if
是引导 if
的一部分,但实际上并不是。 if (int i = 2)
是第一个 else
的主体!
if (int i=1)
|
/ \
/ \
/ \
/ \
/ \
if-block else
|
if(int i=2)
/ \
/ \
/ \
if-block throw i
这反过来意味着什么:
int main () {
if (int i=1) {
} else if (1) {
throw (i+2);
} else {
throw i;
}
}
这段代码是有效的,因为i
声明在throw (i+2);
中可见,但在嵌套作用域中隐藏第一个i
仍然是有效的,因为在嵌套作用域中,名称可以被覆盖:
int main () {
if (int i=1) {
} else if (int i=2) {
throw (i+2);
} else {
throw i;
}
}
总的来说,不要惊慌:使用最后一条语句中找到的模式编写分词器或解析器等仍然有效,这里的相关新知识是条件中的任何声明跨越整个if树,但可以在任何嵌套的if中被覆盖。
此外,请放心,以下内容仍然无效(即使在旧编译器中它是有效的):
if (int i=0) {}
std::cout << i; // nope, not valid
if (auto p = dynamic_cast<Foo*>(q)) { /* ... use p ... */ }
这样的情况,或者任何可能创建空指针的情况。 - Kerrek SB