Document number: N4404
Date: 2015-03-31
Project: Programming Language C++, Language Evolution Working Group
Reply-to: Oleg Smolsky <oleg.smolsky@gmail.com>

Extension to aggregate initialization

I. Introduction

C++11 introduced aggregate initialization and the new succinct syntax is very much appreciated. It removes boiler plate constructor code that is otherwise needed for simple types such as the following:

struct user
{
    uint32_t id_;
    std::string name_;
};

user u1{10, "Alice"};

The Standard states that it is possible to initialize instances of such simple structs provided that there are no base classes. This paper asks for an amendment to allow base classes, provided that they are default-constructible.

II. Motivation

The following case comes up frequently 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_;
};
As seen here, the goal is to be able to construct a status_event instance by using the following natural notation:
post_event(status_event{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 initialize aggregate classes even when they have a trivially constructible base class.

III. Scope

No initialization proposal can be complete without examining basic aggregates of integral types and value initialization. Consider the following case:

struct A { int a1, a2; };
struct B : A { int b1; };

B inst{1};
The most-derived type is aggrigate-initialized here and so the value b1 is reasonably obvious. The base class must be value-initialized in such a case.

A similar situation occurs when mixing user-defined constructors with the compiler-provided ones. Consider the following case:

struct A { int a1, a2 = 1; };
struct B { 
    B() {
        b1 = /* get a value somewhere */;
    }
    int b1;
};
struct C : A, B {
  int c1;
};

C inst{1};
The most-derived type is aggrigate-initialized, while the base classes are initialized with a mix of user-provided constructors, member initializers and value initialization.

IV. Technical specifications

Edit section 8.5.1 "Aggregates [dcl.init.aggr]"

  1. 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), and no virtual functions (10.3) , and only default-constructible base classes.
  2. Default-constructible base classes shall be initialized from an empty initializer list (8.5.4).

V. Acknowledgments

I want to thank Richard Smith for the early feedback and guidance.