Why can't I put a variable declaration in the test portion of a while loop?
Asked Answered
J

6

22

You can, obviously, put a variable declaration in a for loop:

for (int i = 0; ...

and I've noticed that you can do the same thing in if and switch statements as well:

if ((int i = f()) != 0) ...

switch (int ch = stream.get()) ...

But when I try to do the same thing in a while loop:

while ((int ch = stream.get()) != -1) ...

The compiler (VC++ 9.0) does not like it at all.

Is this compliant behavior? Is there a reason for it?

EDIT: I found I can do this:

while (int ch = stream.get() != -1) ...

but because of precedence rules, that's interpreted as:

while (int ch = (stream.get() != -1)) ...

which is not what I want.

Joellenjoelly answered 10/10, 2008 at 10:33 Comment(6)
gcc does not let me declare a variable in "if" statement either, actuallyMackie
Don't you mean while (int ch = (stream.get() != -1)) in the last example?Mock
as an option to get around this, you may want to consider the comma operator: while (int ch, (ch = stream.get()) != -1) {//do stuff here}Expiation
@Steve - yes, of course. edited accordingly.Joellenjoelly
This may be possible for while, if and switch in C++17Retractor
@MarkKCowan Only for if and switch. Also see Why no "while statement with initializer"?Liquate
G
15

The grammar for a condition in the '03 standard is defined as follows:

condition:
  expression
  type-specifier-seq declarator = assignment-expression

The above will therefore only allow conditions such as:

if ( i && j && k ) {}
if ( (i = j) ==0 ) {}
if ( int i = j ) {}

The standard allows the condition to declare a variable, however, they have done so by adding a new grammar rule called 'condition' that can be an expression or a declarator with an initializer. The result is that just because you are in the condition of an if, for, while, or switch does not mean that you can declare a variable inside an expression.

Gabriellagabrielle answered 10/10, 2008 at 15:29 Comment(1)
Right - the key is that a declaration is not an expression.Webber
M
16

The problem is, the standard permits you a declaration inside parenthesis. What you want to do is to get a declaration as part of expression, which is something that standard will not let you do.

while() can have one of two syntaxes: while(<declaration>) or while(<expression>). The declaration uses "=", and looks like expression, but it's a different syntactical entity.

When you write

while(int i = 1) {
}

, that's perfectly fine. "int i=1" is a declaration. However, what you want is

while ( (int i = 1) + 3) {
}

This is a very different animal. You want an expression inside while(), where one of the terms of the expression is a declaration. Now, declaration is a statement, and as such cannot be part of expression. That's why what you need to be done cannot be done.

(after writing the whole rant, I noticed that 2 other people wrote the same thing. Oh well, the more the merrier.)

Musical answered 10/10, 2008 at 21:14 Comment(0)
G
15

The grammar for a condition in the '03 standard is defined as follows:

condition:
  expression
  type-specifier-seq declarator = assignment-expression

The above will therefore only allow conditions such as:

if ( i && j && k ) {}
if ( (i = j) ==0 ) {}
if ( int i = j ) {}

The standard allows the condition to declare a variable, however, they have done so by adding a new grammar rule called 'condition' that can be an expression or a declarator with an initializer. The result is that just because you are in the condition of an if, for, while, or switch does not mean that you can declare a variable inside an expression.

Gabriellagabrielle answered 10/10, 2008 at 15:29 Comment(1)
Right - the key is that a declaration is not an expression.Webber
E
11

This doesn't appear to be compliant behaviour. Part 6.5.1.2 of the standard states:

When the condition of a while statement is a declaration, the scope of the variable that is declared extends from its point of declaration (3.3.1) to the end of the while statement. A while statement of the form

while (T t = x) statement

is equivalent to

label:
{ //start of condition scope
    T t = x;
    if (t) {
        statement
        goto label;
    }
}

So in your example, ch should be declared within the scope of the loop and work correctly (with it being recreated through each loop iteration). Reason for the observed behaviour is most likely due to the compiler not scoping the variable correctly and then declaring it multiple times.

Expiation answered 10/10, 2008 at 10:40 Comment(1)
The compiler is perfectly compliant, it's impossible to do what the question asks about. See my answer below.Musical
F
2

It might be because the contents of the while clause are evaluated each loop, thus it would try and declare "ch" multiple times.

The if, switch, and for loop examples you gave will all have "ch" being defined only once.

Flaunch answered 10/10, 2008 at 10:34 Comment(0)
D
2

You can put a variable declaration in the test expression of a while loop. What you cannot do is put a declaration statement in other expressions. For instance, in the expression a+b+c, you cannot replace b by int i = f(). And the same hold for the expression (a); you can't insert int i=f() to get an expression (int i=f()).

So, in while (int i = foo()), the outermost brackets are part of the while statement, and not of the text-expression, and everything is legal. In while ((int i = foo())), the outermost brackets are still part of the while statement. The test-expression would have the form "(" expr ")", and you end up with a syntax error.

Dabble answered 10/10, 2008 at 14:22 Comment(3)
I don't understand In while ((int i = foo())), the outermost brackets are still part of the while statement. The test-expression would have the form "(" expr ")", and you end up with a syntax error. , specially last sentence in para, explain with better example? , thanks a lot :)Banka
What I understood is I can declare variable x in while e.g while(int x=...) but the initializer part i.e ... must contain the expression not any declaration, right?Banka
@Mr.Anubis: You can't use (( )) because the inner () are an expression themselves, and you can't have a declaration inside that. The outer () belong to the while and don't form an expression. And yes, the initializer for x must be a valid expression itself, and can't contain a second declaration.Dabble
M
0

Try This doesn't work

while (int ch = stream.get(), ch != -1) ...

I've never tried it, but if the comment in your edit is correct, this should work.
VS 2005 won't even compile it.

Maidamaidan answered 10/10, 2008 at 14:59 Comment(3)
This code is interpreted by the compiler as: int ch = (stream.get(), ch != -1)Gabriellagabrielle
According to my sources, the comma operator is at the bottom of the precedence scale - but there's something else broken with my construct.Maidamaidan
Because comma operator doesn't allow declaration statement to appear in it. But assignment would work int i; while(i = 0, ++i); See [expr.comma]Inapprehensible

© 2022 - 2024 — McMap. All rights reserved.