Document number: | P0017R0 |
Date: | 2015-07-13 |
Project: | Programming Language C++, Language Evolution Working Group |
Reply-to: | Oleg Smolsky <oleg.smolsky@gmail.com> |
C++ supports aggregate initialization, thus allowing the following succinct notation:
struct user { uint32_t id_; std::string name_; }; user u1{10, "Alice"};
The Standard states that it is possible to initialize instances of such type but places numerous restrictions (such as "no base classes"). This paper argues for an amendment to relax the rules and put more power (and syntax) into the users' hands.
The original paper, N4404 was discussed in Lenexa and the feedback was generally positive. Several EWG members expressed a strong desire to further extend the functionality, thus prompting another revision.
There are several cases revolving around inheritance of simple aggregate types that frequently come up:
Inheriting from a default-constructible base class.
Comes up when using template-based libraries such as Boost StateChart. The following is needed to define a state with a single member:
struct status_event : boost::statechart::event{ status_event(uint32 seq) : seq_(seq) {} // manually written boiler plate uint32 seq_; };
The goal here is to construct a status_event
instance while writing no boiler plate. So, lets imagine that the base class is the first, very special element of the aggregate:
status_event event{{}, 42};
Inheriting from an aggregate type.
Comes up when people choose to inherit from an aggregate (eg C) type:
struct base { uint32 ibase_; }; struct derived : base { void f(); };
We want to contruct a derived
instance while still explicitly intializing the base clase:
derived d{{42}};
As per 8.5.1 [dcl.init.aggr], it is possible to initialize instances of simple structs and there are provisions for member structs (2), empty members (9) and even brace elision (13). However, Clause (1) states that none of this goodness works when there are base classes.
It would be really useful to extend aggregate initialization rules to cover cases where base classes are present.
Lets consider the following cases:
A single base class:
struct base { int a1, a2; }; struct derived : base { int b1; }; derived d1{{1, 2}, 3}; // full explicit initialization derived d1{{}, 1}; // the base is value initialized
The most-derived type is aggregate-initialized here and so the value b1
is reasonably obvious. The base class must be value-initialized in such a case.
Multiple base classes, mixing user-defined constructors with the compiler-provided ones.
struct base1 { int b1, b2 = 42; }; struct base2 { B() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // full initialization derived d2{{}, {}, 4}; // value-initialized basesThe most-derived type is aggregate-initialized, while the base classes are initialized with a mix of user-provided constructors, member initializers and value initialization.
- The elements of an aggregate are:
-- for an array, the array elements in increasing subscript order
-- for a class, the direct base classes in declaration order followed by the direct members in declaration order
- An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11),
no base classes (Clause 10), andno virtual functions (10.3) and only non-virtual public base classes (10.1). [ Note: Aggregate initialization does not allow inaccessible members to be initialized. This applies to protected and private members as well as protected and private base classes, as the notation treats the latter like special initial elements. —end note ]- When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the
memberselements of the aggregate, inincreasing subscript or memberorder. Eachmemberelement is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.- [no change]
- [no change]
- [no change]
- [no change]
- If there are fewer initializer-clauses in the list than there are
memberselements in the aggregate, then eachmemberelement not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equalinitializer,from an empty initializer list (8.5.4).- [no change]
- If an aggregate class C contains a subaggregate
memberelementme that has no members for purposes of aggregate initialization, the initializer-clause forme shall not be omitted from an initializer-list for an object of type C unless the initializer-clauses for all members of C followingme are also omitted.- [no change]
- [no change]
- 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
memberselements of a subaggregate; it is erroneous for there to be more initializer-clauses thanmemberselements. 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 thememberselements of the subaggregate; any remaining initializer-clauses are left to initialize the nextmemberelement of the aggregate of which the current subaggregate is amemberelement.- All implicit type conversions (Clause 4) are considered when initializing the aggregate member with an assignment-expression. If the assignment-expression can initialize a
memberelement, thememberelement is initialized. Otherwise, if thememberelement is itself a subaggregate, brace elision is assumed and the assignment-expression is considered for the initialization of the firstmemberelement of the subaggregate. [ Note: As specified above, brace elision cannot apply to subaggregates with nomemberselements for purposes of aggregate initialization; an initializer-clause for the entire subobject is required. —end note ]- Note: An aggregate array or an aggregate class may contain
memberselements of a class type with a user-provided constructor (12.1). Initialization of these aggregate objects is described in 12.6.1. —end note ]