C++, variable declaration in 'if' expressi

2019-01-04 09:51发布

What's going on here?

if(int a = Func1())
{
    // Works.
}

if((int a = Func1()))
{
    // Fails to compile.
}

if((int a = Func1())
    && (int b = Func2()))
)
{
    // Do stuff with a and b.
    // This is what I'd really like to be able to do.
}

Section 6.4.3 in the 2003 standard expains how variables declared in a selection statement condition have scope that extends to the end of the substatements controlled by the condition. But I don't see where it says anything about not being able to put parenthesis around the declaration, nor does it say anything about only one declaration per condition.

This limitation is annoying even in cases where only one declaration in the condition is required. Consider this.

bool a = false, b = true;

if(bool x = a || b)
{

}

If I want to enter the 'if"-body scope with x set to false then the declaration needs parenthesis (since the assignment operator has lower precedence than the logical OR), but since parenthesis can't be used it requires declaration of x outside the body, leaking that declaration to a greater scope than is desired. Obviously this example is trivial but a more realistic case would be one where a and b are functions returning values that need to be tested

So is what I want to do non-conformant to the standard, or is my compiler just busting my balls (VS2008)?

8条回答
The star\"
2楼-- · 2019-01-04 10:03

The condition in an if or while statement can be either an expression, or a single variable declaration (with initialisation).

Your second and third examples are neither valid expressions, nor valid declarations, since a declaration can't form part of an expression. While it would be useful to be able to write code like your third example, it would require a significant change to the language syntax.

I don't see where it says anything about not being able to put parenthesis around the declaration, nor does it say anything about only one declaration per condition.

The syntax specification in 6.4/1 gives the following for the condition:

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

specifying a single declaration, with no parentheses or other adornments.

查看更多
冷血范
3楼-- · 2019-01-04 10:08

Here's an ugly workaround using a loop (if both variables are integers):

#include <iostream>

int func1()
{
    return 4;
}

int func2()
{
    return 23;
}

int main()
{
    for (int a = func1(), b = func2(), i = 0;
        i == 0 && a && b; i++)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }

    return 0;
}

But this will confuse other programmers and it's rather bad code, so not recommended.

A simple enclosing {} block (as already recommended) is much easier to read:

{
    int a = func1();
    int b = func2();

    if (a && b)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }
}
查看更多
趁早两清
4楼-- · 2019-01-04 10:10

The last section already works, you just have to write it slightly different:

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}
查看更多
Lonely孤独者°
5楼-- · 2019-01-04 10:16

With a little template magic you can kind of sort of get around the problem of not being able to declare multiple variables:

#include <stdio.h>

template <class LHS, class RHS>
struct And_t {
  LHS lhs;
  RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs && b_rhs;
  }
};
template <class LHS, class RHS> 
And_t<LHS, RHS> And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

template <class LHS, class RHS>
struct Or_t {
LHS lhs;
RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs || b_rhs;
  }
};
template <class LHS, class RHS> 
Or_t<LHS, RHS> Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

int main() {
  if (auto i = And(1, Or(0, 3))) {
    printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs);
  }
  return 0;
}

(Note, this looses the short circuit evaluation.)

查看更多
霸刀☆藐视天下
6楼-- · 2019-01-04 10:17

As of C++17 what you were trying to do is finally possible:

if (int a = Func1(), b = Func2(); a && b)
{
    // Do stuff with a and b.
}

Note the use of ; of instead of , to separate the declaration and the actual condition.

查看更多
神经病院院长
7楼-- · 2019-01-04 10:18

I think you already hinted at the issue. What should the compiler do with this code?

if (!((1 == 0) && (bool a = false))) {
    // what is "a" initialized to?

The "&&" operator is a short-circuit logical AND. That means that if the first part (1==0) turns out to be false, then the second part (bool a = false) should be not be evaluated because it is already known that the final answer will be false. If (bool a = false) isn't evaluated, then what to do with code later on that uses a? Would we just not initialize the variable and leave it undefined? Would we initialize it to the default? What if the data type was a class and doing this had undesirable side effects? What if instead of bool you used a class and it had no default constructor such that the user must provide parameters - what do we do then?

Here's another example:

class Test {
public:
    // note that no default constructor is provided and user MUST
    // provide some value for parameter "p"
    Test(int p);
}

if (!((1 == 0) && (Test a = Test(5)))) {
    // now what do we do?!  what is "a" set to?

Seems like the limitation you have found seems perfectly reasonable - it prevents these kinds of ambiguities from happening.

查看更多
登录 后发表回答