C++17 enhances if and switch statements with the ability to define variables whose life-time is limited to the corresponding scope. This is in keeping with the general guideline that variables should have a tight scope, i.e., should be defined as close to the point of use as possible and should not live beyond where they are really needed.

Prior to C++17, it was possible to define variables in the if statement, but with the condition that for user-defined types, the type must define either operator bool() or operator void *(). C++17 removes this requirement and makes initialization much more simpler and straightforward.

Let us consider the following struct as an example:

Sample Class

An object of this type X is constructed by passing an integer argument. It is a valid initialization if the argument passed is less than 1000, else it is invalid. By the way, this struct has been defined merely to illustrate our idea, and has no other significance.

Take a look at the following function:

Pre-C++17 Usage

It shows the initialization feature available prior to C++17. Notice how we create an object of type X within the condition part of if statement. The scope of the object is just this if statement, extending to the true and false segments of the code.

So how do we know if the initialization is treated as true or false? According to the standard, if the class has operator bool()function, then it will be automatcialy invoked after the object is constructed and if that operator returns true, then the condition evaluates to true, else it is false. If the class does not define operator bool(), but defines operator void *(), then this function will be invoked after construction. If this returns nullptr, the condition is treated as false, else it is true. What if both functions are defined? In that case, operator bool() will be called. What if neither is defined? Then it is a compile-time error!

Here is the output from the program:

operator bool called

Object x_true inited successfully

operator bool called

Object x_false initialization failed

You can see that even though both the operator functions are present, the compiler calls operator bool(). The result is as expected.

Now, what has changed in C++17? The logic has been considerably simplified by introducing a syntax extension to the if statement. Before showing how to take advantage of the new extension, I want to mention that switch statement too has been enhanced in this context.

C++17 Model

As is evident, the if condition can now contain two parts. The first part is the actual initialization (can be skipped if you do not want to initialize any object here) and the second part is the condition part. This is neat because we do not require operator bool() or operator void *() function to tell us if the initialization is successful. The condition part can directly access an appropriate element of the object to trigger the branch to be taken.

Here is the output:

Object x1 inited successfully

Object x2 initialization failed

Object xobj at LEVEL3

Neat, eh?

Object initialization in selection statements, as explained above, is not that common, but I feel it deserves more attention. C++17 makes this usage much more palatable.

I tested this code in Visual Studio Professional 2017, version 15.4.0. The source code is available here.