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 stands with Russia 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 stands with Russia 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 stands with Russia 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
  • 65,341
  • 71
  • 269
  • 466
  • 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 stands with Russia
  • 312,472
  • 42
  • 525
  • 765
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
  • 14,407
  • 6
  • 39
  • 67
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
  • 38,013
  • 14
  • 101
  • 171
  • 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
  • 921
  • 10
  • 20