Understanding Virtual Base Classes and Constructor Calls

Understanding virtual base classes and constructor calls

There is always just one constructor call, and always of the actual, concrete class that you instantiate. It is your responsibility to endow each derived class with a constructor which calls the base classes' constructors if and as necessary, as you did in B's constructor.

Update: Sorry for missing your main point! Thanks to ildjarn.

However, your B inherits virtually from A. According to the standard (10.1.4 in the FIDS), "for each distinct baseclass that is specified virtual, the most derived object shall contain a single base class subobject of that type". In your case this means that when constructing the base, your class F immediately calls A's default constructor, not B's.

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

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.

Why do the constructor of the derived classes want to initialize the virtual base class in C++?

The constructor of virtual base is constructed. It is constructed conditionally. That is, the constructor of the most derived class calls the constructor of the virtual base. If - this is the condition - the derived class with virtual base is not the concrete class of the constructed object, then it will not construct the virtual base because it has already been constructed by the concrete class. But otherwise it will construct the virtual base.

So, you must correctly initialise the virtual base class in constructors of all derived classes. You simply must know that specific initialisation doesn't necessarily happen in case the concrete class is not the one which you are writing. The compiler doesn't and cannot know whether you will ever create direct instances of those intermediate classes, so it cannot simply ignore their broken constructors.

If you made those intermediate classes abstract, then the compiler would know that they are never the most concrete type and thus their constructor would not be required to initialise the virtual base.

Why must virtual base classes be constructed by the most derived class?

Because it avoids this:

class A {
public:
A(int) {}
};

class B0: virtual public A {
public:
B0(): A(0) {}
};

class B1: virtual public A {
public:
B1(): A(1) {}
};

class C: public B0, public B1 {
public:
C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};

wrong constructor called for virtual base class of virtual base class

The Answer is given in the comments by Sam Varshavchik.

The concept of passing the argument from each constructor to it's direct parent was the wrong way of handling the issue.

I was under the wrong impression, that I can only access the direct parents of each class. Sam Varshavchik's comment helped me to see the real problem and look for it in the right places. In one of the many classes, there was one non-virtual inheritance that made it impossible to access the constructor of the base class.

Forced to call the base constructor when using virtual inheritance although it will never be called?

In virtual inheritance, the most-derived class has to directly call all of its ancestor constructors. Since Base doesn't have a default constructor, Middle1() and Middle2() can't compile if they can't pass an int& to Base().

However, in the code you have shown, there is no reason for Base() to take an int by reference. Pass it by value instead, and then Middle1() and Middle2() can pass 0 to Base():

class Base
{
private:
int value;
protected:
Base(int value = 0) { this->value = value; }
};

class Middle1 : virtual public Base
{
protected:
Middle1() { }
};

class Middle2 : virtual public Base
{
protected:
Middle2() { }
};

class Foo : public Middle1, public Middle2
{
public:
Foo(int value) : Base(value) { }
};

Though, I would suggest passing a pointer (or a std::optional) instead:

class Base
{
private:
int value;
protected:
Base(int* avalue = nullptr) { if (avalue) this->value = *avalue; }
};

class Middle1 : virtual public Base
{
protected:
Middle1() { }
};

class Middle2 : virtual public Base
{
protected:
Middle2() { }
};

class Foo : public Middle1, public Middle2
{
public:
Foo(int& value) : Base(&value) { }
};


Related Topics



Leave a reply



Submit