3

This code compiles without warning in GCC 11 and Clang 13 (in C++20 mode)

struct A {
    int x, y;

};

struct B : A { };

int main () {
    A a{1,2};
    B b{3,4};  // Clang 12 wants B b{{3,4}}

    return a.x * b.x + a.y * b.y;

}

but in Clang 12 We get

<source>:10:9: warning: suggest braces around initialization of subobject [-Wmissing-braces]
    B b{3,4};

https://godbolt.org/z/Kdnon9575

Might be related to:

Why does Clang 12 refuse to initialize aggregates in the C++20 way?

and these papers

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1975r0.html

Are single braces officially supported (without warnings) under C++20 for initialising these simple POD Derived classes? If so, where does the standard say that, so we can rely on it?

In C++17 the double braces are required, to avoid warnings, yes?

And in C++14 single braces are a hard error because a derived struct is not an aggregate, yes?

Oliver Schönrock
  • 1,038
  • 6
  • 11

1 Answers1

4

C++17 as well as C++20, https://timsong-cpp.github.io/cppwp/n4659/dcl.init.aggr#12 and https://timsong-cpp.github.io/cppwp/n4868/dcl.init.aggr#15, respectively, allows brace elision for the initialization of aggregates:

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 subaggregate 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.

Class B in the OP's example is an aggregate in both C++17 and C++20 (as per C++17's P0017R1 and its updates primarily to [dcl.init.aggr]/1), where the requirements for what qualifies as an aggregate has been made more lenient with regard to the base class requirement.

An aggregate is an array or a class ([class]) with

  • [...]
  • (C++14) no base classes
  • (C++17 and C++20) no virtual, private, or protected base classes

Now, the Clang diagnostic was only a warning to begin with, and as of Clang 13 it seems to have (imo correctly) have been removed for braced-elided initalization of aggregates that have base classes. It's possible the warning was a remnant corner case from C++14 where B was not an aggregate, but where the program would be ill-formed due to an invalid form of initialization.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • OK, Thank you. So we can say that Clang 12 was simply not yet fully c++20 compliant on the brace elision rules, and clang 13 (and all future compliant compilers) will allow brace elision for Derived Class aggregates? – Oliver Schönrock Jan 11 '22 at 21:15
  • 2
    @OliverSchönrock The diagnostic was only a warning to begin with, but yes it seems Clang has removed it for the case of aggregates whose elements are from a base class (allowed for aggregates in C++20). And no, any base class in C++17 and earlier disqualifies the class as an aggregate: I've expanded the answer with the relevant standardese (from the previously link-only statements). – dfrib Jan 11 '22 at 21:18
  • Thank you for the clarification. So if under c++17 Derived classes are not aggregates, then why does c++17 allow aggregate initialisation of Derived classes at all (with diagnostic with single brace and cleanly with double brace)? Or is that some kind of "other" initialisation? https://godbolt.org/z/nYexcsKof – Oliver Schönrock Jan 11 '22 at 21:25
  • 2
    @OliverSchönrock You were right to being with: the leniency for base class restrictions for aggregate was added already in C++17 as per [p0017r1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0017r1.html), I was citing the wrong working draft (it's n4659 for C++17, not n4140), sorry! Will update the answer: regarding Clang's diagnostic I think the simple answer is "it should never have been there even for C++17, but on the other hand it's a compiler-chosen warning, not an error". – dfrib Jan 11 '22 at 21:54