12 Special member functions [special]

12.9 Inheriting constructors [class.inhctor]

A using-declaration ([namespace.udecl]) that names a constructor implicitly declares a set of inheriting constructors. The candidate set of inherited constructors from the class X named in the using-declaration consists of actual constructors and notional constructors that result from the transformation of defaulted parameters as follows:

  • all non-template constructors of X, and

  • for each non-template constructor of X that has at least one parameter with a default argument, the set of constructors that results from omitting any ellipsis parameter specification and successively omitting parameters with a default argument from the end of the parameter-type-list, and

  • all constructor templates of X, and

  • for each constructor template of X that has at least one parameter with a default argument, the set of constructor templates that results from omitting any ellipsis parameter specification and successively omitting parameters with a default argument from the end of the parameter-type-list.

The constructor characteristics of a constructor or constructor template are

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the complete class where the using-declaration appears or the constructor would be a default, copy, or move constructor for that class. Similarly, for each constructor template in the candidate set of inherited constructors, a constructor template is implicitly declared with the same constructor characteristics unless there is an equivalent user-declared constructor template ([temp.over.link]) in the complete class where the using-declaration appears. [ Note: Default arguments are not inherited. An exception-specification is implied as specified in [except.spec]. — end note ]

A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the corresponding constructor in X is deleted ([dcl.fct.def]). An inheriting constructor shall not be explicitly instantiated ([temp.explicit]) or explicitly specialized ([temp.expl.spec]).

Note: Default and copy/move constructors may be implicitly declared as specified in [class.ctor] and [class.copy].  — end note ]

Example:

struct B1 {
  B1(int);
};

struct B2 {
  B2(int = 13, int = 42);
};

struct D1 : B1 {
  using B1::B1;
};

struct D2 : B2 {
  using B2::B2;
};

The candidate set of inherited constructors in D1 for B1 is

The set of constructors present in D1 is

  • D1(), implicitly-declared default constructor, ill-formed if odr-used

  • D1(const D1&), implicitly-declared copy constructor, not inherited

  • D1(D1&&), implicitly-declared move constructor, not inherited

  • D1(int), implicitly-declared inheriting constructor

The candidate set of inherited constructors in D2 for B2 is

The set of constructors present in D2 is

  • D2(), implicitly-declared default constructor, not inherited

  • D2(const D2&), implicitly-declared copy constructor, not inherited

  • D2(D2&&), implicitly-declared move constructor, not inherited

  • D2(int, int), implicitly-declared inheriting constructor

  • D2(int), implicitly-declared inheriting constructor

 — end example ]

Note: If two using-declarations declare inheriting constructors with the same signatures, the program is ill-formed ([class.mem], [over.load]), because an implicitly-declared constructor introduced by the first using-declaration is not a user-declared constructor and thus does not preclude another declaration of a constructor with the same signature by a subsequent using-declaration. [ Example:

struct B1 {
  B1(int);
};

struct B2 {
  B2(int);
};

struct D1 : B1, B2 {
  using B1::B1;
  using B2::B2;     
};                  // ill-formed: attempts to declare D1(int) twice

struct D2 : B1, B2 {
  using B1::B1;
  using B2::B2;
  D2(int);          // OK: user declaration supersedes both implicit declarations
};

 — end example ]  — end note ]

An inheriting constructor for a class is implicitly defined when it is odr-used ([basic.def.odr]) to create an object of its class type ([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 denoted in the nested-name-specifier of the using-declaration and an expression-list as specified below, and where the compound-statement in its function body is empty ([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 form static_cast<T&&>(p), where p is the name of the corresponding constructor parameter and T is the declared type of p.

Example:

struct B1 {
  B1(int) { }
};

struct B2 {
  B2(double) { }
};

struct D1 : B1 {
  using B1::B1;     // implicitly declares D1(int)
  int x;
};

void test() {
  D1 d(6);          // OK: d.x is not initialized
  D1 e;             // error: D1 has no default constructor
}

struct D2 : B2 {
  using B2::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 T::T;       // declares all constructors from class T
  ~D() { std::clog << "Destroying wrapper" << std::endl; }
};

Class template D wraps any class and forwards all of its constructors, while writing a message to the standard log whenever an object of class D is destroyed.  — end example ]