35

通常我们需要这样的循环

do
{
     Type value(GetCurrentValue());
     Process(value);
}while(condition(value));

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

Type value;
do
{
    value = GetCurrentValue(); 
    Process(value);
}while(condition(value));

我不喜欢这个至少有两个原因。一方面,我喜欢在本地声明事情。其次,如果 value 不可分配或默认构造,而只能复制构造,则这是一个问题。

所以,我的问题有两个方面。首先,我想知道将 do while 的范围扩展到最终条件是否有特定的原因/困难(就像在 for 循环中声明的变量的范围包括 for 循环的主体,尽管它在物理上是括号外)。如果你相信我的第一个问题的答案是“事情就是这样。不要问为什么问题。” 那么我想知道是否有成语可以帮助编写类似于我的示例中的 do-while 循环,但没有我提到的缺点。

希望问题很清楚。

4

2 回答 2

24

如果您想value为 while 循环保留本地范围,您可以这样做:

do
{
     Type value(GetCurrentValue());
     Process(value);
     if (! condition(value) )
         break;
} while(true);

这只是个人喜好,但我发现while类似以下结构的循环更具可读性(while而不是do-while):

while(true) {
    Type value(GetCurrentValue());
    Process(value);
    if (! condition(value) ) {
        break;
    }
}

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

int a = 1;
int b = 2; 
{
    int c = 3;
}
std::cout << a;
std::cout << b;
std::cout << c;

会抱怨c未申报。

至于基本原理 - 这只是一致性问题,“这就是语言的定义方式”

于 2012-11-08T20:30:02.727 回答
2

在do-while 循环体中声明的变量超出其条件表达式的范围是有充分理由的:减少由于未初始化变量而处理未定义行为的可能性。

考虑您的示例代码段的细微变化:

do {
     if (not_ready_yet()) {
         sleep(1);
         continue;
     }
     Type value(GetCurrentValue());
     Process(value);
} while (condition(value)); // error

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

有了它的样子,就不会犯这样的错误。


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

for (int j; j<10; ++j)
    do_something();

或者

int foo(int i) {
    if (i > 10)
        goto end;
    int x = 23;
end:
    return x;
}

要不就

int foo(int i)
{
    int k;
    return k + i + 1;
}

首先,上述原因可能不是驱动 do-while 循环的设计者的原因。


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

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


看看理论上的替代方案,除了改变范围规则只是为了做,像这样的东西可能是一个选择:

do (Type value(GetCurrentValue())) {
     Process(value);
} while (condition(value));

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


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

for (;;) {
     Type value(GetCurrentValue());
     Process(value);
     if (!condition(value))
         break;
}

只比原始代码段多一行。并且打字少于:

do {
     Type value(GetCurrentValue());
     Process(value);
     if (!condition(value))
         break;
} while (true);
于 2021-09-17T08:53:20.227 回答