Search code examples
c++inheritanceconstructorinitializationvirtual-inheritance

Cannot derive from class with virtual inheritance (C++ virtual inheritance)


class Base
{
public:
    Base(std::string arg1, std::string arg2) : arg1(arg1), arg2(arg2) {}
    string arg1,arg2;
    virtual void f() = 0;
    inline virtual ~Base() {}
};

class Mixin1 : virtual public Base
{
};

//Virtual class uses virtual base and a mixin
class VirtualDerived : virtual public Base, public Mixin1
{
public:
    VirtualDerived(string arg2) : Base("some literal", arg2) {}
};

//Here I am concretely subclassing VirtualDerived, whose constructor constructs all virtual superclasses.
class ConcreteDerived : public VirtualDerived
{
public:
    ConcreteDerived(string arg2) : VirtualDerived(arg2) {}
    inline void f() {}
};

int main(int argc, const char * argv[]) {

    ConcreteDerived cd("hello");

    return 0;
}

I am deriving from VirtualDerived, whose constructor will initialize Base. Whether I explicitly declare VirtualDerived's inheritance of Mixin1 virtual or not makes no difference.

The error I get is that ConcreteDerived must explicitly call Base's constructor.

Making ConcreteDerived's inheritance virtual similarly makes no difference.

The problem is not with VirutalDerived's constructor forwarding. If I implement f to make it a concerete class, I can create it without issue:

class VirtualDerived : virtual public Base, public Mixin1
{
public:
    VirtualDerived(string arg2) : Base("some literal", arg2) {}
    inline void f(){}
};

int main(int argc, const char * argv[]) {

    VirtualDerived cd("hello");

    return 0;
}

The compiler seems to understand that VirtualDerived's constructor calls Base's non-trivial constructor when VirtualDerived is created directly. But not if something derives VirtualDerived and calls it. Why is this the case?


Solution

  • The thing to understand about virtual inheritance is that when you virtually inherit from a class, it goes to the "front" of the inheritance "queue"; that is, you must initialise virtual bases before all other base classes.

    In this case, ConcreteDerived has Base as an indirect virtual base, via VirtualDerived. Since Base has no default constructor, you must specify it in the initialiser list of ConcreteDerived.

    The reasoning is this: imagine you had another intermediate class such as

    class VirtualDerived2 : virtual public Base
    {
    public:
        VirtualDerived2() : Base("arg1", "arg2") {}    
    };
    

    Here VirtualDerived2 also virtually inherits from Base, and initialises it with its own set of arguments. Now we add another concrete class:

    class ConcreteDerived2 : public VirtualDerived, public VirtualDerived2
    {
        // ...
    };
    

    Now ConcreteDerived2 has two base classes, both of which virtually inherit from Base. But you only have one copy of Base in the hierarchy -- that's the entire point of virtual inheritance. So now which parameters are you going to use to initialise your (single) copy of Base? Those from VirtualDerived, or those from VirtualDerived2? There is no good answer for this, so C++ makes you choose in the most derived class.