Search code examples
c++structnestedrecordbraced-init-list

Nested Aggregate-Initialization Rule


Given following code:

struct B {int i[2];};
struct A {B b[2];};
int main() {
    A{{0, 0}, {0, 0}}; //too many initializers for ‘A’
    A{{0, 0}, B{0, 0}}; //too many initializers for ‘A’
    A{B{0, 0}, {0, 0}};
    A{B{0, 0}, B{0, 0}};
}

Why would the first two not compile? I think all four lines should mean the same thing since they're all one-to-one mapping?

(The test is compiled using g++ 13.2.0 -std=c++20.)


Solution

  • For example, I don't think A{{0, 0}, {0, 0}} has any ambiguity?

    Let us look at [dcl.init.aggr]/16:

    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 elements of a subaggregate; it is erroneous for there to be more initializer-clauses than elements. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the elements of the subaggregate; any remaining initializer-clauses are left to initialize the next element of the aggregate of which the current subaggregate is an element.

    The parsing seems to go as follows:

      A{{0, 0}, {0, 0}}
    // ^ Entering `A`
      A{{0, 0}, {0, 0}}
    //  ^ Entering `b`
      A{{0, 0}, {0, 0}}
    //   ^ Entering `b[0]`, the opening brace is omitted.
    //   ^ Initializing `b[0].i[0]`
      A{{0, 0}, {0, 0}}
    //      ^ Initializing `b[0].i[1]`
      A{{0, 0}, {0, 0}}
    //       ^ Exiting `b[0]`
    //       ^ Exiting `b`
      A{{0, 0}, {0, 0}}
    //          ^ `A` has no more fields, expected `}`