Defining a variable in the condition part of an if-statement?

C++VariablesIf StatementStandards

C++ Problem Overview


I was just shocked, that this is allowed:

if( int* x = new int( 20 ) )
{
    std::cout << *x << "!\n";
    // delete x;
}
else
{
    std::cout << *x << "!!!\n";
    // delete x;
}
// std:cout << *x; // error - x is not defined in this scope

So, is this allowed by the standard or it's just a compiler extension?


P.S. As there were several comments about this - please ignore that this example is "bad" or dangerous. I know what. This is just the first thing, that came to my mind, as an example.

C++ Solutions


Solution 1 - C++

This is allowed by the specification, since C++98.

From Section 6.4 "Selection statements":

> A name introduced by a declaration in a condition (either introduced by the type-specifier-seq or the declarator of the condition) is in scope from its point of declaration until the end of the substatements controlled by the condition.

The following example is from the same section:

if (int x = f()) {
    int x;    // ill-formed, redeclaration of x
}
else {
    int x;    // ill-formed, redeclaration of x
}

Solution 2 - C++

Not really an answer (but comments are not well suited to code samples), more a reason why it's incredibly handy:

if (int* x = f()) {
    std::cout << *x << "\n";
}

Whenever an API returns an "option" type (which also happens to have a boolean conversion available), this type of construct can be leveraged so that the variable is only accessible within a context where it is sensible to use its value. It's a really powerful idiom.

Solution 3 - C++

It is standard, even in the old C++ 98 version of the language:

enter image description here

Solution 4 - C++

Definition of a variable in the conditional part of a while, if, and switch statement are standard. The relevant clause is 6.4 [stmt.select] paragraph 1 which defines the syntax for the condition.

BTW, your use is pointless: if new fails it throws a std::bad_alloc exception.

Solution 5 - C++

Here is an example demonstrating non typical use of a variable declared in an if condition.

Type of variable is int & which is both convertible to boolean and useable in the then and else branches.

#include <string>
#include <map>
#include <vector>
using namespace std;

vector<string> names {"john", "john", "jack", "john", "jack"};
names.push_back("bill"); // without this push_back, my g++ generated exe fails :-(
map<string, int> ages;
int babies = 0;
for (const auto & name : names) {
    if (int & age = ages[name]) {
        cout << name << " is already " << age++ << " year-old" << endl;
    } else {
        cout << name << " was just born as baby #" << ++babies << endl;
        ++age;
    }
}

output is

john was just born as baby #1
john is already 1 year-old
jack was just born as baby #2
john is already 2 year-old
jack is already 1 year-old
bill was just born as baby #3

Unfortunately, the variable in the condition may only be declared with the '=' declaration syntax.

This rules out other possibly useful cases of types with an explicit constructor.

For instance, next example using an std::ifstream won't compile ...

if (std::ifstream is ("c:/tmp/input1.txt")) { // won't compile!
    std::cout << "true: " << is.rdbuf();
} else {
    is.open("c:/tmp/input2.txt");
    std::cout << "false: " << is.rdbuf();
}

Edited january 2019 ... you now can emulate what I explained could not be done ...

This works for moveable classes like ifstream in C++11 and even for non copiable classes since C++17 with copy elision.

Edited May 2019: use auto to alleviate verbosity

{
    if (auto is = std::ifstream ("missing.txt")) { // ok now !
        std::cout << "true: " << is.rdbuf();
    } else {
        is.open("main.cpp");
        std::cout << "false: " << is.rdbuf();
    }
}
struct NoCpy {
    int i;
    int j;
    NoCpy(int ii = 0, int jj = 0) : i (ii), j (jj) {}
    NoCpy(NoCpy&) = delete;
    NoCpy(NoCpy&&) = delete;
    operator bool() const {return i == j;}
    friend std::ostream & operator << (std::ostream & os, const NoCpy & x) {
        return os << "(" << x.i << ", " << x.j << ")";
    }
};
{
    auto x = NoCpy(); // ok compiles
    // auto y = x; // does not compile
    if (auto nocpy = NoCpy (7, 8)) {
        std::cout << "true: " << nocpy << std::endl;
    } else {
        std::cout << "false: " << nocpy << std::endl;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKiril KirovView Question on Stackoverflow
Solution 1 - C++pb2qView Answer on Stackoverflow
Solution 2 - C++Matthieu M.View Answer on Stackoverflow
Solution 3 - C++user529758View Answer on Stackoverflow
Solution 4 - C++Dietmar KühlView Answer on Stackoverflow
Solution 5 - C++thierry.bravierView Answer on Stackoverflow