This paper is a revision of N2376 "Inheriting Constructors (revision 2)" by Alisdair Meredith, Michael Wong, Jens Maurer.
There is often a desire to initialize a derived class with exactly the same set of constructors as its base. This typically ends up with a series of tediously simply forwarding declarations and definitions. This work could be more easily handled by the compiler, is less error-prone when handled by the compiler, and the intent is clearer to read if the forwarding problem can be reduced to a single statement.
There are also a couple of cases where the inheriting constructors cannot be declared by the user, such as for a dependant base class where a class template derives from one (or more) of its own template type parameters.
Initial analysis was supplied by Francis Glassborow (N1583) and after further feedback from the evolution group, the direction was to word the simplest feature possible without making any effort to support extended use cases. The more complicated examples will probably be handled by a different language feature - a variadic constructor template.
The basic idea is to implicitly declare a set of inheriting constructors with a syntax very similar to a using declaration. Like other implicitly declared constructors, they are only implicitly defined if they are used.
Copy and default constructors are not forwarded, deferring to the existing rules for implicitly declaring copy/default constructors.
As inheriting constructors are implicitly declared, they do not inhibit the implicit declaration of the default constructor.
inheriting constructors retain the throw spec, explicitness, constexpr qualifier and = delete flag of the base constructor.
inheriting constructors are defined in a similar manner to an implicitly defined default constructor, so additional bases and data members are subject to default initialization. Arguments to the base class constructor are 'perfectly forwarded' using rvalue-references.
User declared constructors inhibit forwarding for that particular signature, much as user declaration of a function would hide a specific signature when using declarations are used with regular functions.
If multiple inheriting-constructors-declarations declare the same signature, the program is ill-formed, even if those constructors are not used. This can be worked around with a user-declared constructor instead, which inhibits the problem declarations.
It is often an error for multiple inheriting-constructors-declarations to refer to the same base class. However, if a inheriting-constructor-declaration does not actually lead to any implicit declarations, then there will be no problems if it is ued multiple times, although this is storing up fragile base class problems if constructors are added to that base in the future.
A program is ill-formed if an inheriting constructor is used that forwards to a base class constructor that is declared private. Note that this is different to the using declarations with regular functions, that are ill-formed even if the private function is not used. It is consistent with the way a variadic constructor template would work though.
Typically, inheriting constructor definitions for classes with virtual bases will be ill-formed, unless the virtual base supports default initialization, or the virtual base is a direct base, and named as the base forwarded-to. Likewise, all data members and other direct bases must support default initialization, or any attempt to use a inheriting constructor will be ill-formed. Note: ill-formed when used, not declared.
base_constructors
Add a new keyword to 2.11 Table 3: Keywords
base_constructors
Change the grammar before 9.2p1 as indicated:
member-declaration: decl-specifier-seqopt member-declarator-listopt ; function-definition ;opt ::opt nested-name-specifier templateopt unqualified-id ; using-declaration static_assert-declaration template-declaration inheriting-constructors-declaration
Amend 9.2p1 as indicated:
Create a new section 12.9 class.inherit [not shown in markup here]:Except when used to declare friends (11.4)
or, to introduce the name of a member of a base class into a derived class (7.3.3,11.3), or as a static_assert-declaration (7 dcl.dcl), member-declarations declare members of the class, and each. Each such member-declaration other than an inheriting-constructor-declaration shall declare at least one member name of the class. A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.
12.9 Inheriting Constructors [class.inherit]Change 12.1 class.ctor paragraph 5 as follows:inheriting-constructors-declaration: using base_constructors ::opt nested-name-specifieropt class-name ;An inheriting-constructors-declaration declares a set of inheriting constructors. The class named shall be a direct base class of the class being defined.
For each non-template constructor other than a default or copy constructor in the class named in the inheriting-constructors-declaration, a constructor is implicitly declared with the same parameter-type-list (8.3.5 dcl.fct), exception-specification (15.4 except.spec), absence or presence of
explicit
(12.3.1 class.conv.ctor), and absence or presence ofconstexpr
(7.1.5 dcl.constexpr), unless there is a user- declared constructor with the same signature in the complete class where the inheriting-constructors-declaration appears. Similarly, for each constructor template in the class named in the inheriting-constructors-declaration, a constructor template is implicitly declared with the same template parameter list (14.1 temp.param), parameter-type-list (8.3.5 dcl.fct), exception-specification (15.4 except.spec), absence or presence ofexplicit
(12.3.1 class.conv.ctor), and absence or presence ofconstexpr
(7.1.5 dcl.constexpr) unless there is an equivalent user-declared constructor template (14.5.5.1 temp.over.link) in the complete class where the inheriting-constructors-declaration appears. [Note: Default and copy constructors may be implicitly declared as specified in 12.1 class.ctor and 12.8 class.copy.]A constructor so declared has the same access as the corresponding constructor in the base class. It is deleted if the corresponding base class constructor is deleted (8.4 dcl.fct.def).
An implicitly-declared inheriting constructor for a class is implicitly defined when it is used (3.2 basic.def.odr) to create an object of its class type (1.8 intro.object). An implicitly-defined inheriting constructor performs the set of initializations of the class that would be performed by a user-written
inline
constructor for that class with a mem-initializer-list whose only mem-initializer has a mem-initializer-id that names the base class named in the inheriting-constructors-declaration and an expression-list as specified below, and with an empty compound-statement in its function body (12.6.2 class.base.init). If that user-written constructor would be ill-formed, the program is ill-formed. Each expression in the expression-list is of the formstatic_cast<T&&>(p)
wherep
is the name of the corresponding constructor parameter andT
is the declared type ofp
.[ Example:
Class templatestruct B1 { B1( int ) {} }; struct B2 { B2( double ) {} }; struct D1 : B1 { using base_constructors B1; // implicitly declares D1( int ) int x; }; void test() { D1 d(6); // ok; d.x is not initialized D1 e; // error: base class B1 has no default constructor } struct D2 : B2 { using base_constructors B2; // ok, implicitly declares D2( double ) B1 b; }; D2 f(1.0); // error: B1 has no default constructor template< class T > struct D : T { using base_constructors T; // declares all constructors from class T ~D() { std::clog << "Destroying wrapper" << std::endl; } };
D
wraps any class and forwards all its constructors, while writing a message to the standard log whenever an object of classD
is destroyed.]
If two inheriting-constructors-declarations in a class declare constructors with the same signature, the program is ill-formed (9.2 class.mem, 13.1 over.load). [Note: Since the inheriting constructors declared by the first inheriting-constructors-declaration are not user-declared constructors, the second inheriting-constructors-declaration may attempt to redeclare a constructor.]
[ Example:
]struct B1 { B1( int ); }; struct B2 { B2( int ); }; struct D1 : B1, B2 { using base_constructors B1; using base_constructors B2; // ill-formed implicitly declaring same ctor twice }; struct D2 : B1, B2 { using base_constructors B1; using base_constructors B2; D2( int ); // user declaration prevents conflict };
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, adefaultconstructor having no parameters is implicitly declared. ...
using default T;
syntax.