Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of declarations in the body of a do-while statement

In Why can't you declare a variable inside a do while loop? the OP asks why a declaration in the while-condition of a do-while loop isn't in scope in the do-statement. That would be very unnatural as C/C++ generally follow a "declaration-at-top-of-scope" pattern. But what about the converse - why not extend the scope of any declaration in the do-statement to the while-condition. That would allow

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

to be shortened to

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

which gives a tidy syntax for limiting the scope of the control variable. (Note: I'm aware that this isn't valid syntax - that's the point of the question, why not extend the language to allow this syntax.)

As I note in a comment below, this extension would not break existing code, and would be very much in then spirit of the introduction of for-init (and while-init) scoping.

like image 961
sfjac Avatar asked Dec 13 '14 15:12

sfjac


Video Answer


5 Answers

In your proposal, the while statement while (i != 0); is out of the scope
of the inner block { int i = get_data(); }

It doesn't "see" the variable i, so it doesn't work.

Changing the language to support that would break one of its rules. Scoping is more important than the problem you show, which has a simple solution of declaring the variable before the block.

Also introducing your rule would break existing code as this valid example shows:

int i = 0 ;
do {
  int i = 1 ;
} while (i) ;
like image 138
2501 Avatar answered Oct 20 '22 21:10

2501


You following example will not work because, you are initializing the integer value inside the while loop. Initializing the integer value inside the while loop ensures that the while loop doesn't find i value and will fail.

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

To show you an example:

do {
int i = i+1;

}while(i = 0) 

will give you error:

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

Rationale: See this logic map for help

C++ do...while loop

Standard - The do-while loop is an exit-condition loop. This means that the body of the loop is always executed first. Then, the test condition is evaluated. If the test condition is TRUE, the program executes the body of the loop again. Except that condition is evaluated after the execution of statement instead of before, guaranteeing at least one execution of statement, even if condition is never fulfilled.

But, initializing the conditional statement variable inside the do while loop ensures that conditional statement variable's scope only spans inside the do-while loop and the condition statement in only takes global and local variables giving you a scope error.

Rationale Explanation: Why we shouldn't change:

Not beneficial to making a more efficent program, this is pretty useless not to mention this style of coding is bad because your messing around with scopes. Let say you initialized a variable inside do-while loop then it would cause problems with other functions like loop statements. For example let us say there is two i variables which variable takes part in the conditional statement and loop statement? This extension would causes a logic error.

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

People only need one way to do things why make two different ways?

Conclusion: Adding this extension to the C++ language would cause all kinds of ruckus and errors once people start to mess around with it. So the best solution would be to stick to the standard.

like image 36
Irrational Person Avatar answered Oct 20 '22 22:10

Irrational Person


So currently as a the do while exists the body of the loop is a block:

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

so how does a block scope work, from section 3.3.3 Block scope:

A name declared in a block (6.3) is local to that block; it has block scope. Its potential scope begins at its point of declaration (3.3.2) and ends at the end of its block. A variable declared at block scope is a local variable.

So clearly the scope of i is the block, so you want a rule that would create some sort of special do while block scope. How would that practically work? It would seem the simplest equivalent would be to hoist the declaration to a newly created outer block:

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

which could possibly work fine if all the declarations where at the start of the body, but what about a scenario like this:

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

after hoisting:

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

Another alternative would be to extend the scope from the do while onwards but that would break all sorts of expected behavior. It would break how name hiding works in block scope from section 3.3.10 Name hiding:

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

and example from 3.3.1 shows this:

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

the j in main hides the global j but how would work for our special do while scope?:

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

Extending the scope of the inner j from the do while forwards would surely break a lot of existing code and looking at the previous example there is no intuitive way to hoist the declaration out.

We could play with this an eventually and find something that works but it seems like a lot of work for very little gain and it is completely unintuitive.

like image 1
Shafik Yaghmour Avatar answered Oct 20 '22 20:10

Shafik Yaghmour


Some conjecture:

The c++ standards committee is know to be conservative - c++ is a complex language and they are keen to keep as many confusing edge cases out of the language and standard library as possible.

The rationale is that if there is already a way to express some logic, a language change to support some other way of expressing the logic is unnecessary and potentially confusing.

the loop can already be expressed more safely, with no uninitialised variables like this:

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

so making the argument to extend the scope of variables in a do {} while(); block would be quickly dismissed by the committee as "un-necessary, at risk of adding potentially confusing additions of corner cases"

Beyond that, there is also the issue of lifetime management and destruction order. At the moment, any object created in the do block will be destroyed (in deterministic order) before the while clause is evaluated. If you are going to extend the lifetime of i, would you also extend the lifetime of all objects in the block, thus delaying the execution of destructors? What if i was an object holding a reference to another variable declared in the block? What if the code in the while() depended on a side-effect in the destructor of i?

example (does not compile):

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?
like image 1
Richard Hodges Avatar answered Oct 20 '22 21:10

Richard Hodges


That would not be possible since the variables declared in the block are local in that block. However, the variable i could be declared before the do-while loop. But because we don't want to extend the scope from the loop onwards, we can use a little trick:

First, add this line

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

at the beginning of your code.

Now, you can write

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

or

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

The scope of the i is limited to the loop. The #define does not break the original usage of do-while loop. So the following syntax is still valid:

int j = 0;
do {
    // whatever you want to do with j;
} while (j != 0);
like image 1
DaBler Avatar answered Oct 20 '22 20:10

DaBler