I came across to this example
struct sct
{
int t[2];
};
struct str
{
sct t[2];
};
int main()
{
str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }; //Who does this work?
cout << t[1].t[0].t[1] << t[0].t[1].t[0];
return 0;
}
This compiles and runs fine. It gives the output 34
I expected the syntax for initialization would be:
str t[2] = { { {0, 2},{4, 6} }, { {1, 3},{5, 7} } };
Instead of
{ {0, 2, 4, 6}, {1, 3, 5, 7} };
But this gave:
In function 'int main()':
error: too many initializers for 'str'
Can someone explain why?
This is a picture to illustrate the way I see this:
This looks like a simple typo but the situation is complex enough to tackle it step by step.
First let me show the solution that seems to work:
int main()
{
str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
cout << t[1].t[0].t[1] << t[0].t[1].t[0] << endl;
return 0;
}
So we have an array of str
that holds an array of sct
.
Let's start with the latter. You initialize an array of sct
with something like this:
sct x[2] = { {0, 1}, {2, 3} };
Now for a single instance of str
you could go with
str y = { { {0, 2}, {4, 6} } };
What remains for str t[2]
is to arrange two copies of str
initializing expressions inside curly brackets:
str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
Edited: On the first reading I misunderstood the question. After the post was updated it became clear that the question is on why it is possible to shed two pair of braces but shedding just one pair results in a syntax error.
To understand how the parser is interpreting the code you might like to look at the parse tree. You can make gcc dump trees at several stages of the parser with -fdump-tree-...
options. Here -fdump-tree-original
may be useful.
To avoid additional confusion let's make sure the elements of the structures have different names:
struct sct
{
int a[2];
};
struct str
{
sct b[2];
};
Here is the output I got with GCC 7.5 from
>>>> CODE:
str t[2] = { { 0, 2, 4, 6 }, { 1, 3, 5, 7 } };
>>>> tree enabled by -tree-original
struct str t[2] = {{.b={{.a={0, 2}}, {.a={4, 6}}}}, {.b={{.a={1, 3}}, {.a={5, 7}}}}};
You can see that the compiler adds implicit brackets around the initializing expressions for each structure and around the initializing expressions for each named field.
Now consider the expression that fails to compile:
str t[2] = { { {0, 2},{4, 6} }, { {1, 3},{5, 7} } };
A the upper level the tree for this expression would be
/*Step 1: */ struct str t[2] = { {.b={0, 2}, {4, 6} }, {.b={1, 3}, {5, 7} } };
But as b is an array of sct
, we try to initialize that with {0,2}
getting
sct b[2] = {0, 2};
This expands to
struct sct b[2] = {{.a={0, 2} }};
This is valid C++ since the first element of the array is initialized explicitly and the second element is initialized implicitly with zeros.
With this knowledge we get the following tree
/*Step 2: */ struct str t[2] = { {.b={{.a={0, 2} }}, {4, 6} }, {.b={{.a={1, 3} }}, {5, 7} } };
Now we are left with the following:
struct str z = { { { {0,2} }, { {0,0} } }, {4, 6} };
And the compiler rightfully complains:
error: too many initializers for ‘str’
As a final check consider the following declaration
struct sxc
{
sct b[2];
int c[2];
}
struct sxc z = { {0,2} , {4, 6} };
This compiles and result in the following structure:
{ .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }