Empty braces magic in initializer lists
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
Thought I'd just illustrate:
X d{ { {} }};
| | |
construct an | |
`X` from ... an initializer_list |
containing... int{}
The rules for list-initialization are to find an initializer_list<T>
constructor and use it if at all possible, otherwise... enumerate the constructors and do the normal thing.
With X{{}}
, that is list-initialization: the outermost {}
s are the initializer_list
and this contains one element: the {}
, which is 0
. Straightforward enough (though cryptic).
But with X{{{}}}
, this doesn't work anymore using the outermost {}
as the initializer_list
because you can't initialize an int
from {{}}
. So we fallback to using constructors. Now, one of the constructors takes an initializer_list
, so it's kind of like starting over, except that we'd already peeled off one layer of braces.
This is why for instance vector<int>{{1, 2, 3}}
works too, not just vector<int>{1, 2, 3}
. But like... don't.