Some notes on computer stuff

Flattened aggregate initializers in C and C++

April 28, 2013
[programming] [c] [c++] [initialization]

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’