Search code examples
c++visual-studio-2010pure-virtualvirtual-inheritance

(Why) Is virtual base class constructor call required in pure virtual derived class?


I have a class hierarchy with a diamond structure and a base class without default constructor, since it has reference members.

The code looks as follows:

class Base
{
public:
    Base( CustomType& obj ) : refObj_( obj ) {}
    virtual ~Base() {}
private:
    CustomType& refObj_;
};

class Left : public virtual Base
{
public:
    Left() {}; // ERROR: Compiler requests calling Base's constructor
    virtual void leftsMethod() = 0;
};

class Right : public virtual Base
{
public:
    Right( CustomType& obj ) : Base( obj ) {}; // Compiles, but Base( obj ) never gets called here
    virtual void rightsMethod() = 0;
};

class Bottom : public Left, public Right
{
public:
    Bottom( CustomType& obj ) : Base( obj ), Left(), Right( obj ) {}
};

Edit: Added virtual base destructor (original code has it)

Note that Left and Right are pure virtual classes, i.e. under no circumstances their constructors would call Base's constructor. This is due to virtual inheritance, implying that the most derived class Bottom calls Base's constructor.

My question: Why do I nevertheless have to call Base's constructor in the initialization list of Left? I'd like to avoid this and to pass the reference directly from Bottom's constructor only to Base. (I'm using MSVC 2010s "half C++11" compiler.)

Is there another solution for this combination of virtual inheritance and references in the base class?


Solution

  • Design issues (which clearly exist here) aside, I fully agree with your reasoning that, because Left is an abstract class, no Left constructor will ever have to call any Base constructor and thus it is odd that it is required.

    In fact, I tested your code with several compiler versions and it does compile just fine with gcc 7.1 and onwards, with clang 3.4.1 and onwards, and with all available msvc versions.

    So, my assumption is, that this was just a bug in earlier compiler versions. Can anyone confirm this?

    Also note, if you change virtual void leftsMethod() = 0; to virtual void leftsMethod() {}, so that Left is not abstract anymore, the error returns, even with the latest versions. And this makes perfectly sense, as now you could instantiate a Left instance and thus it would be up to Left's constructor to call one of Base's constructors.

    Possible workaround

    If you cannot switch to a newer compiler and if you cannot alter the implementation of Base, this may be a working solution for you:

    Define a dummy instance of CustomType somewhere. Then, you can provide the "required" default constructors like this:

    class CustomType{};
    
    class Base
    {
    public:
        Base( CustomType& obj ) : refObj_( obj ) {}
    private:
        CustomType& refObj_;
    };
    
    static CustomType dummy;
    
    class Left : public virtual Base
    {
    protected:
        Left() : Base(dummy) {}; // note here
    public:
        virtual void leftsMethod() = 0;
    };
    
    class Right : public virtual Base
    {
    protected:
        Right() : Base(dummy) {}; // and here
    public:
        virtual void rightsMethod() = 0;
    };
    
    class Bottom : public Left, public Right
    {
    public:
        Bottom( CustomType& obj ) : Base( obj ), Left(), Right() {}
        virtual void leftsMethod() override {}
        virtual void rightsMethod() override {}
    };
    
    void test()
    {
        CustomType c;
        Bottom b(c);
    }
    

    This is just to make the compiler happy, your argument that these constructors will never call Base(dummy) still holds.

    Note however, that Base should really have a virtual destructor! I don't know if you just didn't include it here for brevity or if it really doesn't have one. If it doesn't, it's a very bad idea to build a class hierarchy on top of it.