Search code examples
c++diamond-problem

Diamond Problem C++: Derived class of diamond calls default constructor


So as part of the public API of my program I expose class D, such that the user inherits from class D to create their own classes.

However class D is the tip of the deadly diamond and I have run into an issue where the user's classes are calling the default constructor of class A, instead of the parametrized constructor as desired.

  A
 / \
B   C
 \ /
  D
  |
E or F

In the following code, class E is a clean API however calls the wrong A constructor. Class F works as intended, however the user has to add A's parameterised constructor, which is ugly because that class is an internal class.

Is there a way to have class E work as intended? Why is this happening?

#include<iostream>
using namespace std;
class A {
public:
    A(int x)  { cout << "A::A(int ) called" << endl;   }
    A()     { cout << "A::A() called" << endl;   }
};
  
class B : virtual public A {
public:
    B(int x): A(x)   {
       cout<<"B::B(int ) called"<< endl;
    }
};
  
class C : virtual public A {
public:
    C(int x): A(x) {
        cout<<"C::C(int ) called"<< endl;
    }
};
  
class D : public B, public C  {
public:
    D(int x): A(x), B(x), C(x)   {
        cout<<"D::D(int ) called"<< endl;
    }
};

class E : public D {
public:
    E(int x): D(x) {
        cout<<"E::E(int ) called"<<endl;
    }
};

class F : public D {
public:
    F(int x): D(x), A(x) {
        cout<<"F::F(int ) called"<<endl;
    }
};
  
int main()  {
    D d(0);
    cout<<endl;
    E e(1);
    cout<<endl;
    F f(2);
}

Output:

A::A(int ) called
B::B(int ) called
C::C(int ) called
D::D(int ) called

A::A() called
B::B(int ) called
C::C(int ) called
D::D(int ) called
E::E(int ) called

A::A(int ) called
B::B(int ) called
C::C(int ) called
D::D(int ) called
F::F(int ) called

Solution

  • When constructing a class that has any virtual bases, initializers that name a virtual base class are only called for the most derived class. If your class has any virtual base classes, and you want to use the non-default constructor of that virtual base, then you must specify that constructor in the derived class.

    1 "All virtual base subobjects are initialized before any non-virtual base subobject, so only the most derived class calls the constructors of the virtual bases in its member initializer list: ..."

    2 "The initializers where class-or-identifier names a virtual base class are ignored during construction of any class that is not the most derived class of the object that's being constructed."

    This topic is relatively new to me, so I hope anybody with more experience will point out any nuance I missed.