9

一些重构导致了一段代码,这使我得到了这个最小的测试用例:

int main () {
    if (int i=1) {
        /* IF-BLOCK */
    } else {
        throw i;
    }
}

这编译得很好。但是,我一直认为i它只对 可见IF-BLOCK,但似乎不是。这是编译器错误吗?

另外,为什么以下工作?

int main () {
    if (int i=1) {
    } else if (int i=2) {
    } else {
        throw i;
    }
}

注意第二个if“重新声明” i。另一个编译器错误?

4

1 回答 1

15

不,这实际上是正确的行为。

6.4 选择语句[stmt.select]

由条件中的声明引入的名称(由 thetype-specifier-seq或条件的声明者引入)的范围从其声明点到由条件控制的子语句结束。如果名称在由条件控制的子语句的最外层块中重新声明,则重新声明该名称的声明格式不正确。[ 例子:

if (int x = f()) {
    int x; // ill-formed, redeclaration of x
}
else {
    int x; // ill-formed, redeclaration of x
}

—结束示例]

(强调我的)

这基本上意味着 的范围i从条件开始,在if-block 之后结束,其中else-block 也是 -block 的一部分if

您对嵌套的第二个问题if是基于(错误的)假设 anelse-if是 introductory 的一部分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-declaration 在 中可见throw (i+2);,但隐藏第一个 仍然有效i,因为在嵌套范围内,名称可以被覆盖:

int main () {
    if (int i=1) {    
    } else if (int i=2) {
        throw (i+2); // now refers to `int i=2`
    } else {
        throw i;
    }
}

所以总而言之,不要惊慌:使用最后一条语句中找到的模式编写标记器或解析器或其他东西仍然有效,这里的相关新知识是条件中的任何声明跨越整个if-tree,但可以在任何嵌套if的 .

此外,请确保以下内容仍然无效(即使它在旧编译器中有效):

if (int i=0) {}
std::cout << i; // nope, not valid
于 2013-08-14T07:33:38.013 回答