Initializing variables in an "if" statement

It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write

for(int i = 0; i < ... ; ++i) {
   // ...
}

Instead of the variable leaking

int i;
for(i = 0; i < ... ; ++i) {
   // ...
}

Short lived variables are better for several reasons. But to name a couple:

  1. The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.

  2. If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example

    if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
    }
    

If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.


The new form of the if statement has many uses.

Currently, the initializer is either declared before the statement and leaked into the ambient scope, or an explicit scope is used. With the new form, such code can be written more compactly, and the improved scope control makes some erstwhile error-prone constructions a bit more robust.

Open Standard Proposal for If statement with initializer

enter image description here

So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.

I hope it helps!


In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):

if(auto file = std::ifstream("filename"))
{
    // use file here
}
else
{
    // complain about errors here
}

// The identifier `file` does not pollute the wider scope

Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:

if(auto file = std::ifstream("filename"); !file)
{
    // complain about errors here
}
else
{
    // use file here
}

An example might be throwing an exception:

if(auto file = std::ifstream(filename); !file)
    throw std::runtime_error(std::strerror(errno));
else
{
    // use file here
}

Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.


Is there any advantage of using this feature other than making the code shorter?

You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.

Note that you can already do initialization and branching on the result in pre-C++17:

int *get(); // returns nullptr under some condition

if (int *ptr = get())
    doStuff();

This is subject to one's personal opinion, but you can consider an explicit condition more readable:

if (int *ptr = get(); ptr != nullptr)
    doStuff();

Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.

Tags:

C++

C++17