Home > front end >  Diamond Problem C : Derived class of diamond calls default constructor
Diamond Problem C : Derived class of diamond calls default constructor

Time:11-14

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

CodePudding user response:

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.

  • Related