C++98 has a frustratingly large number of ways of initialising an object.
(Note: not all these initialisations may be valid at the same time, or at all. We’re interested in the syntax here, not the semantics of the class X)
One of the design goals in C++11 was uniform initialisation syntax. That is, wherever possible, to use a consistent syntax for initialising any object. The aim was to make the language more consistent, therefore easier to learn (for beginners), and leading to less time spent debugging.
To that end they added brace-initialisation to the language.
As the name would suggest, brace-initialisation uses braces ({}) to enclose initialiser values. So extending the above examples:
There are a couple of highlights from the above code:
Integer i is default-initialised (with the value 0). This is equivalent to C++03’s (much more confusing):
x1 is explicitly default-constructed. This alleviates the ‘classic’ mistake made by almost all C++ programmers at some point in their career:
By extension, this also alleviates C++’s Most Vexing Parse as well. For those not familiar with it, here it is:
Most programmers read this as "create an object, adt, and initialise it with a temporary object, ADT()". Your compiler, however, following the C++ parsing rules, reads it as "adt is a function declaration for a function returning an ADT object, and taking a (pointer to) a function with zero parameters, returning an ADT object."
With brace-initialisation, this problem goes away:
The compiler cannot parse the above except as "create an object, adt, and initialise it with a temporary object, ADT{}"
The uniform initialisation syntax goal means that brace-initialisation can be used anywhere an object must be initialised. This includes the member initialisation list:
In C++98 programmers had the capability to initialise static member variables as part of the class declaration. C++11 extends this to allow default-initialisation of non-static class members. The code:
Can be re-written as:
The member initialiser code ensures that the member variable will always have a default value, even if it is not explicitly initialised in a member initialiser list. For the example above we have told the compiler to provide the default constructor; which does nothing.
When we create object adt1 the (compiler-supplied) default constructor is called. The member initialisers in the class-definition ensure that the members of adt1 are initialised.
Having the initialisers visible at point-of-instantiation gives the compiler the opportunity to optimise (away) constructor calls and create the object in-situ.
More information
Can’t wait? Download the full set of articles as a PDF, here.
To learn more about Feabhas’ Modern C++ training courses, click here.
- Practice makes perfect, part 3 – Idiomatic kata - February 27, 2020
- Practice makes perfect, part 2– foundation kata - February 13, 2020
- Practice makes perfect, part 1 – Code kata - January 30, 2020
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.
He specialises in C++, UML, software modelling, Systems Engineering and process development.
"C++98 has a frustratingly large number of ways of initialising an object."
And C++11 adds one more on top of that.
What I'm frustrated about current way C++ is proceeding, is that people are simply repeating what they've heard somewhere without giving it any thought. Can you really say that C++11 has one and only uniform way of initializing things? No you can't. The (sad) truth is that on top of whole gamut of ways to perform initialization in C++98, C++11 and yet another one. How perverted is that?!?
Do member initializers inhibit the default constructor from being generated?
I've noticed inconsistent behavior from compilers I've tested on.