Search code examples
c++interfacemultiple-inheritancediamond-problem

Diamond inheritance twice derived - constructor params different - base cannot be found with virtual inheritance specified


I struggled to describe the problem succinctly in the title, so please recommend if you understand the issue and have a better title.

I have produced a stripped down version of my problem, its as small as I can get it, and as a result all functions are defined inline, so please excuse, actual code is not like this.

This is my structure:

class iobject
{
public:
    virtual bool isValid (void) = 0;
};

class object : virtual public iobject
{
public:
    object (void) { }
    virtual ~object () {}
    virtual bool isValid (void) { return true; }
};

So above, an interface + implementation which simply tracks whether an object is valid or not... imagine that all objects in an engine/system will derive from this single object.

class ibase : public virtual iobject
{
public:
    virtual void show (void) = 0;
};

class base : public virtual object,
             public virtual ibase
{
public:
    base (int value) : object() { m_value = value; }
    virtual ~base () {}
    virtual void show (void) { std::cout << m_value << std::endl; }

private:
    int m_value;
};

Now I have a hypothetical class, which does nothing more than store a number and prints it. It derives from object. So the 'diamond' is complete.

class derived1 : public virtual base
{
public: 
    derived1 (int value) : base (value) {}
    virtual ~derived1 () {}
};

Now I derive from the base class. The thing to note about this derived class is that I have the same constructor parameters as the base class (int value).

class derived2 : public virtual derived1
{
public:
    derived2 (void) : derived1 (15) {}
    virtual ~derived2 () {}
};

Finally I derive from this derived class. However, note that this class does not have any constructor parameters. Instead, this class internally should know that the value should be 15 in this example.

My expectation is that when I instantiate derived2, it will construct derived1 with the value 15 and pass it through the object. I would hope to do the following:

derived1 works(1234);
derived2 doesNotWork;

works.show();
doesNotWork.show();

But when I attempt this, I get:

error C2512: 'base::base' : no appropriate default constructor available

If I add an empty base::base constructor then I end up having m_value undefined as I expected (but tried anyway)

I seem to be missing something obvious..., can someone highlight it?


Solution

  • Change the derived2 to this:

    derived2(void) : derived1(15), base(15) {}
    

    The same issue came-up here: Why is Default constructor called in virtual inheritance?

    And the answer is described in this FAQ: http://www.parashift.com/c++-faq-lite/virtual-inheritance-ctors.html

    In short: C++ does not call virtual constructors hierarchically. The most derived class is responsible for calling all the virtual base class constructors. So you must explicitly call derived1() and base() since they take parameters. The linked FAQ implies that the best practice here is to make the virtual base classes only have no-arg constructors.