Consider the following minimal example:
#include <iostream>
struct X {
X() { std::cout << "Default-ctor" << std::endl; }
X(std::initializer_list<int> l) {
std::cout << "Ilist-ctor: " << l.size() << std::endl;
}
};
int main() {
X a{};
X b({}); // reads as construct from {}
X c{{}}; // reads as construct from {0}
X d{{{}}}; // reads as construct from what?
// X e{{{{}}}}; // fails as expected
}
Godbolt example
I have no questions about a, b and c, everything is rather clear
But I can not understand why d works
What this additional pair of braces in d stands for? I looked up C++20 standard, but I can not find answer easily. Both clang and gcc agree on this code, so it is me who misses something
What do empty brackets mean in struct declaration? T object {}; is syntax for value initialization (4). Without the curly brackets, the object would be default initialized. To be pedantic, this is not a "struct declaration". It is declaration of a variable.
Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment.
The most common benefit of doing this is improved performance. If the expression whatever is the same type as member variable x_, the result of the whatever expression is constructed directly inside x_ — the compiler does not make a separate copy of the object.
An initializer list starts after the constructor name and its parameters. The list begins with a colon ( : ) and is followed by the list of variables that are to be initialized – all of the variables are separated by a comma with their values in curly brackets.
A nice trick to do to get information about what the compiler does, is to compile using all errors:
-Weverything
. Let's see the output here (for d
only):
9.cpp:16:6: warning: constructor call from initializer list is incompatible with C++98
[-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~~~~~
X::X(std::initializer_list)
is called.
9.cpp:16:8: warning: scalar initialized from empty initializer list is incompatible with
C++98 [-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~
Scalar (int
) initialized in inner {}
. So we have X d{{0}}
.
9.cpp:16:7: warning: initialization of initializer_list object is incompatible with
C++98 [-Wc++98-compat]
X d{{{}}}; // reads as construct from what?
^~~~
5 warnings generated.
std::initializer_list
is initialized from {0}
. So we have X d{std::initializer_list<int>{0}};
!
This shows us everything we need. The extra bracket is for constructing the initializer list.
Note: If you want to add extra brackets you can by invoking the copy/move constructor (or eliding it), but C++ compilers won't do it implicitly for you to prevent errors:
X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With