3

I want to create an if where a variable is declared, assigned and checked. If the variable's value is acceptable, I want to use it inside if body. Here's an example of how I thought I could do that:

if ((int result = Foo()) != 0) {
    // use result
}

I assumed that Foo() returns some value, which is assigned to result, and returned by assignment operator =, and finally checked against 0 in != 0. Unfortunately, it results in a compilation error:

main.cpp:31:10: error: expected primary-expression before ‘int’
if ((int i = Foo()) != 0)
     ^                                          
main.cpp:31:10: error: expected ‘)’ before ‘int’

Why is this error happening? And what ways could there be to fix it?

Saage
  • 363
  • 1
  • 3
  • 12
  • 1
    declare `int result` OUTSIDE of the if, e.g. before you try to use it. – Marc B May 20 '15 at 18:55
  • if you're fine abusing the language, you could do something like struct Foo { operator int() { return 42; } }; for (int i = Foo(); i != 0;) { /* do stuff */ break; } – wonko realtime May 20 '15 at 18:59
  • Well, your logic would apply if you really had "assignment operator" in your code. But you don't. The `=` is not an operator at all and `int result = Foo()` is not an expression. It is a declaration. Declaration are not expressions, which requires special treatment for this situation and imposes restrictions on its usability. – AnT May 20 '15 at 19:05
  • 1
    Related question: http://stackoverflow.com/questions/190748/why-cant-i-put-a-variable-declaration-in-the-test-portion-of-a-while-loop – Ferruccio May 20 '15 at 19:05
  • You can't declare variables in expressions, which is the main reason why it fails. – David G May 20 '15 at 19:05
  • "The = is not an operator at all." - isn't it? Care to elaborate? – Saage May 20 '15 at 19:05
  • 1
    Related: http://stackoverflow.com/questions/11217179/double-as-true-false – Captain Giraffe May 20 '15 at 19:06
  • 1
    similar quesion : http://stackoverflow.com/questions/14620898/how-does-one-declare-a-variable-inside-an-if-statement – Alexander May 20 '15 at 19:07
  • @Saage: What you have in your code is an *initialization*, not an assignment. When `=` is used in initialization, it is not an assignment operator at all, it is just a formal syntactic element of initialization syntax. It has no relation to assignment operator whatsoever. Just like the comma in declaration `int a,b;` has nothing to do with comma operator, `=` in initialization syntax has nothing to do with assignment operator. – AnT May 20 '15 at 19:07
  • Indeed, I forgot about that! But still, initialization returns the value of the just-assigned variable, does it not? So the only reason my code doesn't work is because the standard defined `if` syntax in way that doesn't allow my trick, right? – Saage May 20 '15 at 19:13
  • 1
    @Saage: No, it doesn't. In C++ only *expressions* can "return" something. Declaration is not an expression (regardless of whether it includes an initializer or not). Declarations cannot be used as expressions. They don't "return" anything. – AnT May 20 '15 at 19:17

5 Answers5

7

The logic is supported, but declaring a variable within an if statement and using it this way is not. The reason is related to the fact that an initializer works differently than a regular assignment, but working around this is easy and trivial.

Just do something like this instead.

int result;

if ((result = Foo()) != 0) {
    // use result
}
Jonathan Wood
  • 61,921
  • 66
  • 246
  • 419
  • 2
    ...and add additional scope if you want `result` to have a specific lifetime tied to the `if` statement. – Captain Obvlious May 20 '15 at 18:57
  • 3
    "declaring a variable within an if statement is not supported" - it is actually. Writing if (int result = Foo()) { /*use result*/ } compiles and works as expected. – Saage May 20 '15 at 18:59
  • 3
    This is incorrect, you can declare a variable within the condition of an if statement, draft standard section `6.4p3` – Shafik Yaghmour May 20 '15 at 19:01
3

Your reasoning seems to be based on the assumption that = in

if ((int result = Foo()) != 0) 

is an assignment operator and that int result = Foo() is "just an expression" that evaluates to something.

This is not true.

The int result = Foo() part is not an expression in C++. It is a declaration with an initializer. The = in initializer syntax is not an assignment operator at all. It is just a syntactic element that coincidentally uses the same character as assignment operator. The int result = Foo() is not an expression and it does not "evaluate" to any result.

Because if the above, support for something like

if (int result = Foo())

requires special treatment, which severely limits the flexibility of this syntax. What you tried in your code goes outside the bounds of what's allowed by that special treatment.

AnT
  • 302,239
  • 39
  • 506
  • 752
2

Bjarne uses this construct as a scope restrictor in 6.3.2.1 The C++ programming language as a recommendation.

Use:

if (int result = Foo()) {
    // use non-zero result
}

It is particularly useful with pointers

if (Foo* result = GetFoo()) {
    // use valid Foo
}

The !=0 part is redundant as truthiness is !=0.

The extended construct with the comparison is not allowed.

Further discussion of this construct from here

Community
  • 1
  • 1
Captain Giraffe
  • 13,890
  • 5
  • 38
  • 65
0

It fails because it's illegal. It's also ugly. @Jonathan Wood suggested declaring the variable outside the if. I suggest calling Foo outside, too:

int result = Foo();
if(result!=0) ...

The (x=f())!=y construct, while legal as an if condition, only makes sense in a loop, where

`while((c=getchar())!='\n) ... do something with c ...

Is the shorter and nicer equivalent of

c = getchar();
while(c!='\n')
{
    ...
    c = getchar(c);
}

It saves writing the call to getchar() twice. It saves nothing when used in an if, so there's no point in using it.

zmbq
  • 36,789
  • 13
  • 91
  • 160
  • I just don't see why it is illegal. The real reason I tried this construct was because I tried to check if a certain key-value pair existed in a map, for example ((map::iterator it = mymap.find(1)) != mymap.end() && it->second == 1) { /* do stuff*/ }. It is not beautiful, but it kind of makes sense to do that in a single statement. – Saage May 20 '15 at 19:04
0

Try this:

if ( int i = (Foo() != 0) ? Foo() : 0 ){
        cout << "Hello. Number i = " << i;
    }   
Emiliano Sangoi
  • 905
  • 10
  • 20