17

Perhaps I am getting rusty (have been writing in Python recently).

Why does this not compile?

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

without the () around the int i=f() I get another, much more reasonable error of i is not being boolean. But that's why I wanted the parentheses in the first place!

My guess would be that using the parentheses makes it into an expression, and that declaration statements are not allowed in an expression. Is it so? And if yes, is it one of the C++'s syntax quirks?

BTW, I was actually trying to do this:

if ( (Mymap::iterator it = m.find(name)) != m.end())
    return it->second;
davka
  • 13,974
  • 11
  • 61
  • 86

4 Answers4

38

You can declare a variable in the if statement in C++ but it is restricted to be used with direct initialization and it needs to convert to a Boolean value:

if (int i = f()) { ... }

C++ doesn't have anything which could be described as "declaration expression", i.e. [sub-] expressions declaring a variable.

Actually, I just looked up the clause in the standard and both forms of initialization are supported according to 6.4 [stmt.select] paragraph 1:

...
condition:
   expression
   attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
   attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list
...

That is, it is also be possible to write:

if (int i{f()}) { ... }

Obviously, this only works in C++2011 because C++2003 doesn't have brace-initialization.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I don't think your last example is legal. You can only do `if (int i {f()}) { /*...*/ }` or `if (int i = f()) { /*...*/ }`. _braced-init-list_ must include braces. ( _braced-init-list_ is "{ _initializer-list_ ,OPT }" or "{ }"). – CB Bailey Jan 22 '12 at 14:02
  • @CharlesBailey: yes, you are right: I somehow mistook the "braced-init-list" to mean (...). I'll fix this. – Dietmar Kühl Jan 22 '12 at 14:10
  • thanks, sounds reasonable, and @Ilya's answer below gives some rationale – davka Jan 22 '12 at 15:24
  • 2
    From your first sentence "but it is restricted to be used with direct initialization *and it needs to convert to a Boolean value*" -> can you please explain this in easy sentence? (I guess the value initialized works as Boolean value, right? but couldn't understand the restriction part also from your first line) , thanks – Mr.Anubis Feb 06 '12 at 18:06
20

There's a problem with scope.

Consider the following code:

if ((int a = foo1()) || (int b = foo2()))
{
    bar(b);
}

Is b declared inside the block? What if foo1() returns true?

Ilya Kogan
  • 21,995
  • 15
  • 85
  • 141
  • forgot to mention that I don't think this is a scope issue as `()` AFAIK does not create scope. Am I mistaken? – davka Jan 22 '12 at 13:49
  • 3
    @SoapBox but what if foo1 returns 1 and by short-circuit evaluation (which is guaranteed as far as I know) the assignation of `b` is skipped? – lccarrasco Jan 22 '12 at 14:08
  • Exactly - You can't propose allowing variables to be declared in () unless you address the scope issue; variables in C++ have scope that begins when their declaration is 'executed', which may never occur in this situation. – greggo Mar 05 '14 at 18:32
5

You can declare a variable in an if statement (or in for or while), but only in the outer parenthesis block and it needs to be able to be converted to bool.

Your guess is basically right, it's not allowed because

(int i = 42;)

is not a valid declaration with initialization.

You need one additional line,

Mymap::iterator it;
if ( (it = m.find(name)) != m.end())
    return it->second;

but then it's better to write

Mymap::iterator it = m.find(name);
if ( it != m.end() ) 
    return it->second;

You can put the return line after the if, if you really want this line back, at least for me this doesn't harm readability, but others might see that different.

If you really, really want to declare an iterator and use the it as bool in an if condition, you could do

if ( struct { int it; operator bool() { return it != m.end; } } s = { m.find(name) } )
    return s.it->second;

but I would consider this harmful ;-)

Gunther Piez
  • 29,760
  • 6
  • 71
  • 103
0

It's true that you can't write

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

but you can perfectly write

if ( int i=f())

So you can use the && operator to perform both operations in one statement like

if ( int i=1 && (i=f()) == 0)

i should be initialized with any value other than 0, and it should be the first condition if your compiler applies left-to-right evaluation.

But unfortunately, that's not applicable in case of iterators as your second example asks.

Islam Hassan
  • 1,736
  • 4
  • 26
  • 42