为什么while while的条件超出了范围

Arm*_*yan 22 c++ scope do-while

我们经常需要像这样的循环

do
{
     Type value(GetCurrentValue());
     Process(value);
}while(condition(value));
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不会编译,因为value范围结束于}.这意味着我必须在循环之外声明它.

Type value;
do
{
    value = GetCurrentValue(); 
    Process(value);
}while(condition(value));
Run Code Online (Sandbox Code Playgroud)

至少有两个原因,我不喜欢这个.首先,我喜欢在当地宣布事物.第二,如果值不可赋值或默认可构造,则这是一个问题,但只能复制构造.

所以,我的问题有两个方面.首先,我想知道将do while的范围扩展到最终条件是否有特定的原因/困难(正如for循环中声明的变量的范围包括for循环的主体,尽管它实际上是在牙箍之外).如果你相信我的第一个问题的答案是"这就是它的方式.不要问为什么问题." 然后我想知道是否有成语可以帮助编写类似于我的例子中的do-while循环,但没有我提到的缺点.

希望问题清楚.

sam*_*hen 15

如果你想保持valuewhile循环的本地范围,你可以这样做:

do
{
     Type value(GetCurrentValue());
     Process(value);
     if (! condition(value) )
         break;
} while(true);
Run Code Online (Sandbox Code Playgroud)

这只是个人偏好,但我发现while循环结构如下更可读(while而不是do-while):

while(true) {
    Type value(GetCurrentValue());
    Process(value);
    if (! condition(value) ) {
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

C/C++中的作用域规则的工作原理如下:在大括号{...}块内声明的局部变量只对该块是本地/可见的.例如:

int a = 1;
int b = 2; 
{
    int c = 3;
}
std::cout << a;
std::cout << b;
std::cout << c;
Run Code Online (Sandbox Code Playgroud)

会抱怨c未申报.

至于理由 - 这只是一致性问题,"这就是语言的定义方式"

  • @EdS.,约翰:OP说'如果你相信我的第一个问题的答案是"这就是它的方式.不要问为什么问题."**那么我想知道是否有成语可以帮助编写类似于我的例子中的do-while循环,但没有我提到的缺点.**' (8认同)
  • 我将问题解释为“‘for’循环有一种特殊情况,允许在初始化变量的地方(在控制括号中)声明变量,但仍然表现得好像它是在主体的范围内声明的一样。为什么`while` 循环是否没有类似的特殊情况,允许在主体中声明的变量在控制括号中可见?” (我这样解释是因为这是我想知道的:-) (2认同)

max*_*zig 6

do-while 循环体内声明的变量超出其条件表达式的范围有一个很好的理由:减少处理由于未初始化变量而导致的未定义行为的可能性。

考虑示例片段的细微变化:

do {
     if (not_ready_yet()) {
         sleep(1);
         continue;
     }
     Type value(GetCurrentValue());
     Process(value);
} while (condition(value)); // error
Run Code Online (Sandbox Code Playgroud)

如果 C++ 允许在循环条件中使用循环作用域变量,则这种跳转 (via continue) 到条件表达式将产生未定义的行为,因为它访问未初始化的变量(value在我们的示例中)。

既然如此,就不可能犯这样的错误。


由于 C++ 允许通过多种方式使用未初始化的变量,例如

for (int j; j<10; ++j)
    do_something();
Run Code Online (Sandbox Code Playgroud)

或者

int foo(int i) {
    if (i > 10)
        goto end;
    int x = 23;
end:
    return x;
}
Run Code Online (Sandbox Code Playgroud)

要不就

int foo(int i)
{
    int k;
    return k + i + 1;
}
Run Code Online (Sandbox Code Playgroud)

上述原因可能并不是 do-while 循环设计者的首要动机。


采用当前方式可以简化编译器(和语言),因为作为 do-while 循环体的复合语句具有与所有其他复合语句完全相同的作用域规则。

对于必须处理有限资源的早期 C 编译器来说,这可能是一个强有力的论点。由于 C++ 是基于 C 构建的,因此有强烈的动机不改变此类基本设计决策。


看看理论上的替代方案,除了只是为了 do-while 改变作用域规则之外,类似这样的事情也可能是一种选择:

do (Type value(GetCurrentValue())) {
     Process(value);
} while (condition(value));
Run Code Online (Sandbox Code Playgroud)

然而,这可能会让人们对是否value每次迭代都重新初始化感到困惑。


不过,使用当前的语言,像这样重写它并不算太糟糕:

for (;;) {
     Type value(GetCurrentValue());
     Process(value);
     if (!condition(value))
         break;
}
Run Code Online (Sandbox Code Playgroud)

只比原来的代码片段多一行。并且需要输入的内容少于:

do {
     Type value(GetCurrentValue());
     Process(value);
     if (!condition(value))
         break;
} while (true);
Run Code Online (Sandbox Code Playgroud)