This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-11-11


2799. Inheriting default constructors

Section: 11.4.5.2  [class.default.ctor]     Status: drafting     Submitter: Hubert Tong     Date: 2017-09-01

(See also submission #545.)

Consider:

  struct A { int n; };
  struct B : A {
    using A::A;
    B(int);
  };

Does B have a default constructor?

Suggested resolution [SUPERSEDED]:

  1. Change in 9.9 [namespace.udecl] paragraph 4 as follows:

    If a constructor or assignment operator brought from a base class into a derived class has the signature of a default constructor or copy/move constructor or assignment operator for the derived class (11.4.5.2 [class.default.ctor], 11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]), the using-declaration does not by itself suppress the implicit declaration of the derived class member; the member from the base class is hidden or overridden by the implicitly-declared copy/move constructor or assignment operator special member function of the derived class, as described below.
  2. Change in 11.4.5.2 [class.default.ctor] paragraph 1 as follows:

    A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no user-declared constructor for class X, or if X inherits (9.9 [namespace.udecl]) one or more default constructors and there is no user-declared default constructor for X, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.5 [dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class.

Proposed resolution [SUPERSEDED]:

(This also resolves issue 2632.)

  1. Change in 9.9 [namespace.udecl] paragraph 4 as follows:

    If a constructor or assignment operator brought from a base class into a derived class has the signature of a copy/move constructor or assignment operator for the derived class (11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]), the using-declaration does not by itself suppress the implicit declaration of the derived class member; the member from the base class is hidden or overridden by the implicitly-declared copy/move constructor or assignment operator of the derived class, as described below. [ Note: A using-declarator that names a member function of a base class does not suppress the implicit declaration of a special member function in the derived class, even if their signatures are the same (11.4.5.2 [class.default.ctor], 11.4.5.3 [class.copy.ctor], 11.4.6 [class.copy.assign]). -- end note ]
  2. Add a new paragraph before 11.4.1 [class.mem.general] paragraph 2 as follows:

    ... For any other member-declaration, each declared entity that is not an unnamed bit-field (11.4.10 [class.bit]) is a member of the class, termed a user-declared member, and each such member-declaration shall either declare at least one member name of the class or declare at least one unnamed bit-field.
  3. Change in 11.4.5.2 [class.default.ctor] paragraph 1 as follows:

    A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no a class X does not have a user-declared constructor for class X, or if X inherits (9.9 [namespace.udecl]) one or more default constructors and X does not have a user-declared default constructor, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.5 [dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class. [ Example:
      struct A {};
      struct B {};
      struct C : A, B {
        using A::A, B::B;
        C(int);
      };
      C c;     // OK
    
      struct X { X(int = 0, int = 0); };  // #1
      struct Y { Y(int = 0); };           // #2
      struct Z : X, Y {
        using X::X, Y::Y;
      };
      Z z2(1, 1);   // OK, invokes X(1, 1) and Y()
      Z z1(1);      // error: ambiguous between #1 and #2
      Z z0;         // OK, invokes X() and Y()
    
    
    -- end example ]
  4. Change in 11.4.5.3 [class.copy.ctor] paragraph 6 as follows:

    If the class definition does not explicitly declare have a user-declared copy constructor, a non-explicit one is declared implicitly. ...
  5. Change in 11.4.5.3 [class.copy.ctor] paragraph 8 as follows:

    If the definition of a class X does not explicitly declare have a user-declared move constructor, a non-explicit one will be implicitly declared as defaulted if and only if ...
  6. Add a new paragraph before 11.4.5.3 [class.copy.ctor] paragraph 11 as follows:

    [ Note: A using-declaration in a derived class C that names a constructor from a base class never suppresses the implicit declaration of a copy/move constructor of C, even if the base class constructor would be a copy or move constructor if declared as a member of C. -- end note]

    A copy/move constructor for class X is trivial if it is not user-provided and if: ...

  7. Change in 11.4.6 [class.copy.assign] paragraph 2 as follows:

    If the class definition does not explicitly declare have a user-declared copy assignment operator, one is declared implicitly. If the class definition declares has a user-declared move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defaulted (9.5 [dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor (D.7 [depr.impldec]). ...

Additional notes (November, 2023)

How does access checking interact with the proposed resolution above?

  struct B {
  protected:
    B(int = 0);
  };
  struct A : B {
    using B::B;
    A(void *);
  };
  A a;       // okay?
  A aa(42);  // not okay

CWG 2023-11-06

CWG resolved not to declare a default constructor in the derived class, but instead apply the usual rules for inherited constructors for this case. The wording should be changed so that the presence of a default constructor is never checked, in particular for "trivial class" (11.2 [class.prop], fixed by P3247R1 (Deprecate the notion of trivial types)), vacuous initialization (6.7.3 [basic.life] paragraph 1, fixed by issue 2859), and value initialization (9.4.1 [dcl.init.general] paragraph 9, also fixed by issue 2859).