Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An inheriting constructor is a C++ mechanism that allows a derived class to expose the constructors of its direct base class in its own scope via a using declaration. Rather than generating implicit forwarding wrappers (as was originally specified in C++11), modern C++ (since C++17) makes the base class constructors directly visible to overload resolution when initializing the derived class.

Syntax

The feature is invoked by placing a using declaration with the base class name inside the derived class definition.
#include <string>

class Base {
public:
    Base(int x);
    Base(const std::string& s, double d);
};

class Derived : public Base {
public:
    // Introduces Base constructors into Derived's overload set
    using Base::Base; 
};

Technical Mechanics and Rules

1. Overload Resolution and Shadowing When using Base::Base; is declared, all constructors from the base class become visible during overload resolution for the derived class. However, if the derived class explicitly declares a constructor with the exact same parameter signature as an inherited constructor, the derived class’s constructor hides (shadows) the inherited one. 2. Derived Member Initialization When an inherited constructor is invoked, it only initializes the base class subobject. It does not accept arguments for derived class members. Data members specific to the derived class are initialized exclusively via default member initializers (in-class initialization). If a derived member lacks a default initializer, it undergoes default initialization (which leaves scalar types uninitialized).
class Derived : public Base {
public:
    using Base::Base;
    
    int initialized_member{42}; // Safely initialized
    int uninitialized_member;   // Contains garbage data if an inherited constructor is used
};
3. Handling of Default Arguments In modern C++, default arguments are not expanded into multiple overloaded signatures of varying arity. Instead, the base constructor is inherited as a single entity. If the inherited constructor is selected during overload resolution, the default arguments defined in the base class are evaluated in the context of the base class when the constructor is invoked. 4. Address and Instantiation Restrictions As with all constructors in C++, it is fundamentally impossible to take the address of an inherited constructor. Furthermore, because inherited constructors are not generated as distinct new functions within the derived class, they cannot be explicitly instantiated or explicitly specialized. 5. Access Specifiers and Qualifiers Inherited constructors retain the access level (public, protected, or private), the explicit specifier, and the constexpr specifier of the original base class constructors. The access specifier applied to the using declaration itself in the derived class is ignored by the compiler for the inherited constructors. 6. Special Member Functions Visibility Under C++17 rules, the using declaration makes the base class’s default, copy, and move constructors visible to the derived class. However, inherited copy and move constructors are explicitly excluded from the candidate set during overload resolution when initializing a derived object. For the default constructor, if the derived class has an implicitly declared default constructor, it hides (shadows) the inherited default constructor; they do not compete in overload resolution. Conversely, if the derived class declares a user-defined constructor (e.g., Derived(int)), the implicit default constructor is suppressed. In this scenario, the inherited default constructor is not hidden and will successfully be used to default-construct the derived class. 7. Multiple Inheritance Conflicts If a class inherits from multiple base classes that possess constructors with identical signatures, the using declarations themselves are perfectly valid and the class definition will compile. An ambiguity error only occurs later during overload resolution if an initialization actually attempts to invoke the conflicting signature. To resolve this potential ambiguity, the derived class must explicitly declare a constructor with the conflicting signature, which hides the conflicting inherited constructors entirely.
class BaseA { public: BaseA(int); };
class BaseB { public: BaseB(int); };

class Derived : public BaseA, public BaseB {
public:
    using BaseA::BaseA;
    using BaseB::BaseB; // Valid class definition in C++17
    
    // Resolves ambiguity if an initialization matching this signature (e.g., Derived d(5);) is attempted
    Derived(int x) : BaseA(x), BaseB(x) {} 
};
Master C++ with Deep Grasping Methodology!Learn More