Under the new standard, a derived class can reuse the constructors defined by its direct base class. Although, as we’ll see, such constructors are not inherited in the normal sense of that term, it is nonetheless common to refer to such constructors as “inherited.” For the same reasons that a class may initialize only its direct base class, a class may inherit constructors only from its direct base. A class cannot inherit the default, copy, and move constructors. If the derived class does not directly define these constructors, the compiler synthesizes them as usual.
A derived class inherits its base-class constructors by providing a using
declaration that names its (direct) base class. As an example, we can redefine our Bulk_quote
class (§15.4, p. 610) to inherit its constructors from Disc_quote
:
class Bulk_quote : public Disc_quote {
public:
using Disc_quote::Disc_quote; // inherit Disc_quote's constructors
double net_price(std::size_t) const;
};
Ordinarily, a using
declaration only makes a name visible in the current scope. When applied to a constructor, a using
declaration causes the compiler to generate code. The compiler generates a derived constructor corresponding to each constructor in the base. That is, for each constructor in the base class, the compiler generates a constructor in the derived class that has the same parameter list.
These compiler-generated constructors have the form
derived(parms) : base(args) { }
where derived is the name of the derived class, base is the name of the base class, parms is the parameter list of the constructor, and args pass the parameters from the derived constructor to the base constructor. In our Bulk_quote
class, the inherited constructor would be equivalent to
Bulk_quote(const std::string& book, double price,
std::size_t qty, double disc):
Disc_quote(book, price, qty, disc) { }
If the derived class has any data members of its own, those members are default initialized (§7.1.4, p. 266).
Unlike using
declarations for ordinary members, a constructor using
declaration does not change the access level of the inherited constructor(s). For example, regardless of where the using
declaration appears, a private
constructor in the base is a private
constructor in the derived; similarly for protected
and public
constructors.
Moreover, a using
declaration can’t specify explicit
or constexpr
. If a constructor in the base is explicit
(§7.5.4, p. 296) or constexpr
(§7.5.6, p. 299), the inherited constructor has the same property.
If a base-class constructor has default arguments (§6.5.1, p. 236), those arguments are not inherited. Instead, the derived class gets multiple inherited constructors in which each parameter with a default argument is successively omitted. For example, if the base has a constructor with two parameters, the second of which has a default, the derived class will obtain two constructors: one with both parameters (and no default argument) and a second constructor with a single parameter corresponding to the left-most, non-defaulted parameter in the base class.
If a base class has several constructors, then with two exceptions, the derived class inherits each of the constructors from its base class. The first exception is that a derived class can inherit some constructors and define its own versions of other constructors. If the derived class defines a constructor with the same parameters as a constructor in the base, then that constructor is not inherited. The one defined in the derived class is used in place of the inherited constructor.
The second exception is that the default, copy, and move constructors are not inherited. These constructors are synthesized using the normal rules. An inherited constructor is not treated as a user-defined constructor. Therefore, a class that contains only inherited constructors will have a synthesized default constructor.