为什么`for`语句范围的特殊规则?

650*_*502 33 c++

我最近在这个问题上跌跌撞撞

for(int i=0,n=v.size(); i<n; i++) {
   ...
   P2d n = ...   <<<--- error here
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨n已经定义了局部变量的事实,尽管open括号看起来应该开始一个新的范围.

事实上,该标准对此有一个特殊的措辞,虽然使用g ++ 4.6.3编译好的代码,它抱怨更新的版本和其他编译器.

这条特殊规则背后的理由是什么(如果有的话)?

更清楚:标准解释说这是不允许的,我对这个错误的技术原因没有任何疑问:我只是想知道为什么委员会决定使用特殊的额外规则而不是仅仅创建另一个嵌套范围看到开口支撑(就像在其他地方发生的那样).

例如,为了使代码合法,你可以用两个支撑对包裹身体而不是一个...

还请注意for/while/if,虽然被认为是良好实践,但后面的括号不是强制性的,也不是语法的一部分,但仍然存在包含循环变量的范围(因此使用函数定义作为另一个示例,其中本地的范围是函数不相关:函数体不是语句,括号是强制性的).

在C++语法中,a的主体for只是一个声明; 但是如果这个语句碰巧是一个支撑组,那么它会得到一个特殊的处理for/while/if(当你使用支撑组作为语言中的其他语句时不会发生这种情况).

将这种额外的复杂性添加到语言中的原因是什么?它显然不是必需的,只是将括号视为另一个内部范围似乎(对我而言)更简单.

是否存在这种更简单,更常规的方法不起作用的用例?

请注意,我不是在征求意见.要么你知道为什么委员会做出了这个决定(在标准中也需要一个非常精细的措辞,而不是仅仅将正文作为常规处理括号封闭块作为陈述),或者你不这样做.

编辑

语法的"单一范围"视图对我来说是不自然的,但在技术上可以用于for可以合理化为具有反向goto语句的单个块的语句,但是在if语句的非常类似的情况下很难防御:

if (int x = whatever()) {
    int x = 3; // Illegal
} else {
    int x = 4; // Illegal here too
}
Run Code Online (Sandbox Code Playgroud)

但这是合法的

if (int x = whatever()) {
    int z = foo();
} else {
    int z = bar();
}
Run Code Online (Sandbox Code Playgroud)

声明的条件,then部分和else部分是否具有if相同的范围?不,因为你可以声明两个z变量.它们是独立的范围吗?不,因为你无法申报x.

我能看到的唯一合理化是,thenelse部分确实是单独的范围,但是添加了(奇怪的)规则,在条件中声明的变量不能在范围中声明.为什么存在这个额外的奇怪限制规则就是我所要求的.

Kug*_*uBu 19

int i = 0;
for (MyObject o1; i<10; i++) {
   MyObject o2;
}
Run Code Online (Sandbox Code Playgroud)

可以从最近编译器的角度转换为:

int i = 0;
{
    MyObject o1;
    Label0:
    MyObject o2; //o2 will be destroyed and reconstructed 10 times, while being with the same scope as o1
    i++;
    if (i < 10)
        goto Label0;
}
Run Code Online (Sandbox Code Playgroud)

这是你最后一个问号的答案,它们没有添加复杂的东西,只是在同一范围内使用goto标记,而不是转到范围之外然后再次输入.我没有看到为什么它更好的明确原因.(虽然它会与旧代码不兼容)


MSa*_*ers 18

语义对于for循环来说并不特殊!if (bool b = foo()) { }工作原理相同.奇怪的是{ }它本身就是一个块.如果没有引入新范围,那将毫无用处.因此,明显的不一致是由于异常案例的错误概括.

[edit] 另一种观点是考虑一个假设的可选关键字:

// Not a _conditional_ statement theoretically, but grammatically identical
always()
{
    Foo();
}
Run Code Online (Sandbox Code Playgroud)

这统一了规则,你不会期望这里有三个范围(内部,中间,外部).

[编辑2](请不要将此作为移动目标来回答)

你想知道生命和范围(两个不同的东西)

int i = 0;
for (MyObject o1; i<10; i++) {
   MyObject o2;
}
Run Code Online (Sandbox Code Playgroud)

让我们概括一下:

MyObject o2; // Outer scope
int i = 0;
for (MyObject o1; i<o1.fooCount(); i++) {
   std::cout << o2.asString();
   MyObject o2;
}
Run Code Online (Sandbox Code Playgroud)

显然,在所有迭代中,调用o2.asString()指向外部o2.它不像内部o2存在循环迭代.当名称尚未在内部作用域中定义时,名称查找不会使用外部作用域中的名称 - 而"尚未定义"是编译时的事物.内部的重复构造和破坏o2是运行时的事情.

  • @MSalters:当退出包含它的范围时,INNER o2将被销毁.但它与`o1`所包含的范围相同,但是`o1`没有被破坏.流程是否退出该范围?为什么对于'for`认为重要的所有这些特殊处理?这是个问题.有哪些用例证明了这种增加的逻辑复杂性? (2认同)

Meh*_*dad 5

看它是这样的:
一对大括号可以隐藏内部可见的变量封闭对大括号(或全球)的:

void foo(int n)
{
    // the containing block
    for (int i = 0; i < n; ++i)
    {
        int n = 5;  // allowed: n is visible inside the containing { }
        int i = 5;  // not allowed: i is NOT visible inside the containing { }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果以这种方式思考,你会发现这里没有特殊的规则.


Dan*_*ani -2

你的问题是 for 的定义部分被认为在 for 的范围内。

         // V one definition
for(int i=0,n=v.size(); i<n; i++) {
   ...
    // V second definition
   P2d n = ...   <<<--- error here
}
Run Code Online (Sandbox Code Playgroud)