Today I found an interesting statement on one of forum pages of LOR: initializers of inner aggregates can be flattened.
Consider this example:
#include <cstddef> #include <cstdlib> #include <iostream> #include <string> struct item { const char c; const int i; const std::string s; }; int main(void) { const item items[3] = { { 'a', 0, "A" }, { 'b', 1, "B" }, { 'c', 2 } }; for (std::size_t i = 0; i < sizeof(items)/sizeof(items[0]); ++i) { std::cout << "c[" << i << "]: " << items[i].c << '\n' << "i[" << i << "]: " << items[i].i << '\n' << "s[" << i << "]: " << items[i].s << std::endl; } return EXIT_SUCCESS; }
The output of a compiled example is easy to guess:
c[0]: a i[0]: 0 s[0]: A c[1]: b i[1]: 1 s[1]: B c[2]: c i[2]: 2 s[2]:
Now lets remove inner curly braces in initialization of the items
array by
replacing
const item items[] = { { 'a', 0, "A" }, { 'b', 1, "B" }, { 'c', 2 } };
with
const item items[] = { 'a', 0, "A", 'b', 1, "B", 'c', 2 };
It works the same way as the first example! If you want the compiler to warn
you when such initialization takes place, pass -Wall
or -Wmissing-braces
argument to gcc to get such warnings:
flattened.cpp:20:5: warning: missing braces around initializer for ‘const item’ [-Wmissing-braces] flattened.cpp:20:5: warning: missing braces around initializer for ‘const item’ [-Wmissing-braces] flattened.cpp:20:5: warning: missing braces around initializer for ‘const item’ [-Wmissing-braces]
The most interesting part of this is that it’s not one of compiler’s extensions, it’s a well documented behaviour. Here is what ANSI C standard says about it (3.5.7 Initialization):
If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the members of the subaggregate or the first member of the contained union. Otherwise, only enough initializers from the list are taken to account for the members of the first subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next member of the aggregate of which the current subaggregate or contained union is a part.
Here is what draft #3242 of C++11 standard says (8.5.1/11):
In a declaration of the form T x = { a }; braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializer-clauses than members. If, however, the initializer-list for a sub-aggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member.
But looks like this doesn’t work with unified curly brace initializers in C++11:
#include <cstddef> #include <cstdlib> #include <iostream> #include <string> struct item { item(int i, int j) :i(i) ,j(j) { } const int i; const int j; }; int main(void) { const item items[] = { 1, 2, 3, 4, 5, 6, }; for (std::size_t i = 0; i < sizeof(items)/sizeof(items[0]); ++i) { std::cout << "i[" << i << "]: " << items[i].i << '\n' << "j[" << i << "]: " << items[i].j << std::endl; } return EXIT_SUCCESS; }
Compiling of the example code above with -std=c++11
flag passed to gcc results
in:
flattened-ctors.cpp: In function ‘int main()’: flattened-ctors.cpp:25:5: error: could not convert ‘1’ from ‘int’ to ‘const item’ flattened-ctors.cpp:25:5: error: could not convert ‘2’ from ‘int’ to ‘const item’ flattened-ctors.cpp:25:5: error: could not convert ‘3’ from ‘int’ to ‘const item’ flattened-ctors.cpp:25:5: error: could not convert ‘4’ from ‘int’ to ‘const item’ flattened-ctors.cpp:25:5: error: could not convert ‘5’ from ‘int’ to ‘const item’ flattened-ctors.cpp:25:5: error: could not convert ‘6’ from ‘int’ to ‘const item’