do-while声明正文中的声明范围

时间:2014-12-13 15:35:21

标签: c++ declaration language-lawyer do-while

Why can't you declare a variable inside a do while loop?中,OP询问为什么do-while循环的while条件中的声明不在do语句的范围内。这将是非常不自然的,因为C / C ++通常遵循“顶级声明”模式。但是反过来 - 为什么不将do语句中的任何声明的范围扩展到while条件。这将允许

int i;
do {
  i = get_data();
  // whatever you want to do with i;
} while (i != 0);

缩短为

do {
  int i = get_data();
  // whatever you want to do with i;
} while (i != 0);

为限制控制变量的范围提供了一个整洁的语法。 (注意:我知道这不是有效的语法 - 这就是问题的关键,为什么不扩展语言以允许这种语法。)

正如我在下面的评论中指出的那样,这个扩展不会破坏现有代码,并且非常符合引入for-init(和while-init)范围的精神。

5 个答案:

答案 0 :(得分:2)

在您的提案中,while语句while (i != 0);超出了内部区块{ int i = get_data(); }

的范围

它没有"看"变量i,所以它不起作用。

更改支持的语言会破坏其中一条规则。范围界定比您显示的问题更重要,它有一个在块之前声明变量的简单解决方案。

同样引入您的规则会破坏现有代码,因为此有效示例显示:

int i = 0 ;
do {
  int i = 1 ;
} while (i) ;

答案 1 :(得分:2)

以下示例将不起作用,因为您正在初始化while循环内的整数值。初始化while循环内的整数值可确保while循环不会找到i值并将失败。

do {
  int i = get_data();
  // whatever you want to do with i;
} while (i != 0);

为您举例:

do {
int i = i+1;

}while(i = 0) 

会给你错误:

prog.cpp:8:12: error: ‘i’ was not declared in this scope
     }while(i = 0);

理由: 请参阅此逻辑映射以获取帮助

C++ do...while loop

标准 - do-while循环是退出条件循环。这意味着循环体始终首先执行。然后,评估测试条件。如果测试条件为TRUE,程序将再次执行循环体。除了在执行语句而不是之前评估条件之外,保证至少执行一次语句,即使条件永远不会满足。

但是,在do while循环中初始化条件语句变量可确保条件语句变量的作用域仅跨越do-while循环,而condition语句只接受全局和局部变量,从而给出作用域错误。

理由说明: 为什么我们不应该改变:

制作一个更有效的程序并不是一件好事,这对于这种编码风格很糟糕是没有用的,因为你搞乱了范围。假设您在do-while循环中初始化了一个变量,那么它会导致其他函数(如循环语句)出现问题。例如,让我们说有两个i变量,哪个变量参与条件语句和循环语句?此扩展会导致逻辑错误。

int i = 0;
do
{
    int i = 0;
    i = i+1;
}
while(i < 10);
for(i; i < 20;i++)
{
    code...
}

人们只需要一种方法来做两种不同的方式吗?

结论:一旦人们开始乱用它,将这个扩展添加到C ++语言会引起各种各样的骚动和错误。因此,最好的解决方案是坚持标准。

答案 2 :(得分:1)

一些猜想:

c ++标准委员会被认为是保守的--c ++是一种复杂的语言,他们热衷于尽可能多地保留语言和标准库中令人困惑的边缘情况。

基本原理是,如果已经有一种表达某种逻辑的方法,那么语言改变以支持其他表达逻辑的方式是不必要的,并且可能会造成混淆。

循环已经可以更安全地表达,没有像这样的未初始化变量:

for(;;) {
  int i = get_data(); 
  // whatever you want to do with i;
  if (!i)
    break;
}

因此,委员会很快就会认为扩大do {} while();区块中变量范围的论点是“不必要的,有可能增加可能引起混淆的角落案件”

除此之外,还有终身管理和销毁秩序的问题。目前,在评估do子句之前,while块中创建的任何对象将被销毁(以确定的顺序)。如果要延长i的生命周期,是否还会延长块中所有对象的生命周期,从而延迟析构函数的执行?如果i是一个对象,持有对块中声明的另一个变量的引用,该怎么办?如果while()中的代码依赖于i的析构函数中的副作用怎么办?

示例(不编译):

do {
  object1 o1;
  object2 i(o1); // holds reference to o1 
  object3 x;
  ...
} while(i.test_for_end()); // implicit scope extension. 
// what if test_for_end() depends on the destructor of x?

答案 3 :(得分:0)

所以目前作为do while存在,循环体是一个块:

do 
{  // begin block
   int i = get_data();
  //    whatever you want to do with i;
}  //end block
while (i != 0);

那么块范围是如何工作的,来自3.3.3 块范围

  

块(6.3)中声明的名称是该块的本地名称;它有块范围。它的潜在范围始于它   声明点(3.3.2)并在其块结束时结束。在块作用域中声明的变量是本地的   变量

很明显i的范围是块,所以你想要一个规则,在块范围内创建某种特殊的do。这怎么会有效?似乎最简单的等价物是将声明提升到新创建的外部块:

{
  int i = get_data(); // hoist declaration outside
  do 
  {  // begin block
    //    whatever you want to do with i;
  }  //end block
  while (i != 0);
}

如果身体开始处的所有声明都可以正常工作,但是这样的场景怎么样:

int k = 0 ;
do 
{
   int i = get_data();
   k++ ;        // side effect before declaration
   int j = k ;  // j it set to 1
}
while (i != 0);

吊装后:

int k = 0 ;
{
  int i = get_data();
  int j = k ;  // j is set to 0
  do 
  {
     k++ ;   
  }
  while (i != 0);
}

另一种选择是将范围从do开始扩展,但这会破坏各种预期行为。它将破坏名称隐藏在块3.3.10 名称隐藏中的块范围内的工作方式:

  

可以通过在嵌套声明性区域中显式声明相同名称或派生名称来隐藏名称   class(10.2)。

来自3.3.1的示例显示了这一点:

int j = 24;
int main() {
    int i = j, j;
    j = 42;
}

j中的main隐藏了全局j,但是对于我们在范围内的特殊操作有何用处?:

int j = 24;
do {
    int i = j, j;
    j = 42;
} while( j != 0 )

将内部j的范围从do向前扩展肯定会破坏许多现有代码,并且在前面的示例中没有直观的方法来提升声明。

我们最终可以玩这个,找到有用的东西,但看起来很多工作只是收获很少,而且完全不直观。

答案 4 :(得分:0)

这是不可能的,因为块中声明的变量在该块中是本地的。 但是,变量i可以在do-while循环之前声明。 但是因为我们不希望从循环开始扩展范围,所以我们可以使用一个小技巧:

首先,添加此行

#define do(cond) switch (cond) do default:

在代码的开头。

现在,你可以写

do (int i = get_data()) {
    // whatever you want to do with i;
} while ((i = get_data()) != 0);

do (int i = 0) {
    i = get_data();
    // whatever you want to do with i;
} while (i != 0);

i的范围仅限于循环。 #define不会破坏do-while循环的原始用法。 因此以下语法仍然有效:

int j = 0;
do {
    // whatever you want to do with j;
} while (j != 0);