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未申报.
至于理由 - 这只是一致性问题,"这就是语言的定义方式"
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)