21

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.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
  • gcc does not let me declare a variable in "if" statement either, actually – Alex B Oct 10 '08 at 10:42
  • Don't you mean while (int ch = (stream.get() != -1)) in the last example? – Steve Fallows Oct 10 '08 at 12:21
  • 2
    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} – workmad3 Oct 10 '08 at 14:31
  • @Steve - yes, of course. edited accordingly. – Ferruccio Oct 10 '08 at 14:48
  • This may be possible for `while`, `if` and `switch` in C++17 – Mark K Cowan Jan 21 '17 at 16:34
  • 1
    @MarkKCowan Only for [`if` and `switch`](https://meetingcpp.com/blog/items/final-features-of-c17.html). Also see [Why no "while statement with initializer"?](https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/9YzUGq8yTBI) – manlio Jan 12 '18 at 16:54

6 Answers6

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.)

14

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.

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
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.

workmad3
  • 25,101
  • 4
  • 35
  • 56
  • The compiler is perfectly compliant, it's impossible to do what the question asks about. See my answer below. –  Oct 10 '08 at 21:07
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.

RB.
  • 36,301
  • 12
  • 91
  • 131
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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 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 :) – Mr.Anubis Aug 01 '12 at 03:47
  • 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? – Mr.Anubis Aug 01 '12 at 03:50
  • 1
    @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. – MSalters Aug 01 '12 at 12:30
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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • This code is interpreted by the compiler as: int ch = (stream.get(), ch != -1) – Richard Corden Oct 10 '08 at 15:30
  • According to my sources, the comma operator is at the bottom of the precedence scale - but there's something else broken with my construct. – Mark Ransom Oct 10 '08 at 22:08
  • 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] – Yola Mar 08 '19 at 07:10