0

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
Will T
  • 43
  • 5
  • Does this answer your question? [Understanding virtual base classes and constructor calls](https://stackoverflow.com/questions/6461784/understanding-virtual-base-classes-and-constructor-calls) – JohnFilleau Nov 13 '21 at 05:10

1 Answers1

1

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.

JohnFilleau
  • 4,045
  • 1
  • 15
  • 22
  • Ah, so doesn't seem like there is a solution to my issue – Will T Nov 13 '21 at 04:32
  • @Will I feel like there *should* be a solution though. I haven't run into this myself. Maybe you actually want E and F to have a member variable of type D? – JohnFilleau Nov 13 '21 at 05:03
  • @Will "Generally, virtual base classes are most suitable when the classes that derive from the virtual base, and especially the virtual base itself, are pure abstract classes." https://isocpp.org/wiki/faq/multiple-inheritance#virtual-inheritance-abcs in which case the base class constructor will take no parameters (which is a good thing and would solve your problem) – JohnFilleau Nov 13 '21 at 05:32
  • Thanks for the suggestions @JohnFilleau. Composition would require quite some refactoring, but might be a cleaner solution overall. Class A is currently close to being a purely abstract interface with the exception of 1 property, a sort of identifier, which it makes sense for B and C to have if they are inherited from directly (which they can be), or D/E/F to have one of. The identifier is used as part of A's setup, so it's not easy to extract from the constructor. I think I'll give composition a go. – Will T Nov 14 '21 at 04:11