Show
With Modern C++ and each revision of the Standard, we get more comfortable ways to initialize data members. There’s non-static data member initialization (from C++11) and inline variables (for static members since C++17). In this blog post, you’ll learn how to use the syntax and how it has changed over the years. We’ll go from C++11, through C++14, and C++17 until C++20. Updated in July 2022: added more examples, use cases, and C++20 features. Initialisation of Data membersBefore C++11, if you had a class member, you could only initialize it with a default value through the initialization list in a constructor.
Since C++11, the syntax has been improved, and you can initialize
As you can see, the variables get their default value in the place of declaration. There’s no need to set values inside a constructor. The feature is called non-static data member initialization, or NSDMI in short. What’s more, since C++17, we can initialise static data members thanks to inline variables:
There’s no need to define Please note that for constant integer static fields ( Let’s explore those useful features: NSDMI and inline variables. We’ll see the examples and how the features improved over the years. NSDMI - Non-static data member initializationIn short, the compiler performs the initialization of your fields as you’d write it in the constructor initializer list.
Let’s see this in detail: How It worksWith a bit of “machinery,” we can see when the compiler performs the initialization. Let’s consider the following type:
The implementation of
This allows us to see when the code is called. For example:
And the use:
The output:
In the second case, for As you might already guess, the compiler performs the initialization of the fields as if the fields were initialized in a “member initialization list.” So they get the default values before the constructor’s body is invoked. In other words the compiler expands the code:
into
How about other constructors? Copy and Move ConstructorsThe compiler initializes the fields in all constructors, including copy and move constructors. However, when a copy or move constructor is default, there’s no need to perform that extra initialization. See the examples:
And the use case:
The output:
See code here @Wandbox. The compiler initialized the fields with their default values in the above example. That’s why it’s better also to use the initializer list inside a copy constructor:
We get:
The same happens if you rely on the copy constructor generated by the compiler:
You get a similar behavior for the move constructor. Other forms of NSDMILet’s try some other examples and see all options that we can initialize a data member using NSDMI:
See @Compiler Explorer. Here’s the summary:
C++14 Updates for aggregates, NSDMIOriginally, in C++11, if you used default member initialisation then your class couldn’t be an agregate type:
I was unaware of this issue, but Shafik Yaghmour noted that in the comments below the article.
Fortunately, it’s fixed in C++14, so
Compiles as expected; see @Wandbox C++20 Updates for bit fieldsSince C++11, the code only considered “regular” fields… but how about bit fields in a class?
This is only a recent change in C++20 that allows you to write:
The proposal was accepted into C++20 as Default Bit Field Initialiser for C++20 P0683. The case with autoSince we can declare and initialize a variable inside a class, there’s an interesting question about You can use
But not as a class non-static member:
Unfortunately,
While static members are just static variables, and that’s why it’s relatively easy for the compiler to deduce the type, it’s not that easy for regular members. This is mostly because of the possible cyclic dependencies of types and the class layout. If you’re interested in the full story, you can read this great explanation at cor3ntin blog: The case for Auto Non-Static Data Member Initializers | cor3ntin. The case with CTAD - Class Template Argument DeductionSimilarly, as with It works for static variables:
But not as a non-static-member:
On GCC 10.0 I get
Advantages of NSDMI
Any negative sides of NSDMI?On the other hand, the feature has some limitations and inconveniences:
Do you see any other issues? Inline Variables C++17So far, we have discussed non-static data members. Do we have any improvements for declaring and initializing static variables in a class? In C++11/14, you had to define a variable in a corresponding cpp file:
Fortunately, with C++17, we also got inline variables, which means you can define a
One note: before C++17, you could
declare and define a constant static integer data member, but since C++17 it’s “extended” to all types (and also mutable) through the
The compiler guarantees that there’s precisely one definition of this static variable for all translation units, including the class declaration. Inline variables are still static class variables so that they will be initialized before the The feature makes it much easier to develop header-only libraries, as there’s no need to create cpp files for static variables or use some hacks to keep them in a header file. Here’s the full example at @Wandbox SummaryIn this article, we reviewed how in-class member initialization changed with Modern C++. In C++11, we got NSDMI - non-static data member initialization. You can now declare a member variable and init that with a default value. The initialization will happen before each constructor body is called, in the constructor initialization list. NSDMI improved with C++14 (aggregates) and in C++20 (bit fields are now supported). The feature is also reflected in C++ Core Guidelines:
What’s more, in C++17, we got inline variables, which means you can declare and initialize a static member without the need to do that in a corresponding cpp file. Here’s a “summary” example that combines the features:
Play at @Wandbox For simplicity, Your Turn
Even more in a book and a course!The topic of data member initialization was so interesting to me that I followed the rabbit hole and investigated many related areas. In summary, I created a book with almost 200 pages where you can learn about special member functions (constructors, destructors, copy, move), and various ways of object initialization, all across C++11 up to C++20.
Data Member Initialization in Modern C++ @Leanpub Leanpub offers a 60-day refund period! Buy together with my C++ Lambda Story ebook: Buy C++Lambda Story and Data Members in C++, 14.99$ instead of 29.98$ If you like, you can also take a simplified version of the book and look at my interactive Educative minicourse: See here: Initializing Data Members: From C++11 till C++20 17 short lessons, interactive code samples and more!
What happens if I use a variable before initializing it to a value?Notice that a variable that is not initialized does not have a defined value, hence it cannot be used until it is assigned such a value.
What will happen if you use the variable without initializing it?An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.
What happens if you use a variable without initializing it C ++?If you don't initialize an variable that's defined inside a function, the variable value remain undefined. That means the element takes on whatever value previously resided at that location in memory.
Can we initialize a variable before we declared it?Variables can either be initialized in the same statement as the declaration or later in the code.
|