Why Is Default Constructor Called in Virtual Inheritance

Why is Default constructor called in virtual inheritance?

When using virtual inheritance, the virtual base class's constructor is called directly by the most derived class's constructor. In this case, the daughter constructor directly calls the grandmother constructor.

Since you didn't explicitly call grandmother constructor in the initialization list, the default constructor will be called. To call the correct constructor, change it to:

daugther(int attr) : grandmother(attr), mother(attr) { ... }

See also This FAQ entry.

C++ Virtual inheritance and constructors

There are two big keys here:

  1. Virtual base classes are initialized directly by the most derived class constructor, and not indirectly by other base classes.

  2. Virtual base classes are initialized before any non-virtual base classes.

So let's look at your examples.

When setting only B:A to virtual I get:

A() B(2) A(3) C(3) D()

That makes some sense - B inherits A virtually, and therefore, unless specifically stated otherwise, B(int x) calls A's default constructor.

B does not call A's constructor at all. D calls A directly for the virtual instance of A. But since the constructor of D doesn't specify how to initialize A, you get A's default constructor.

The A in B(int i) : A(i) gets ignored not because A is a virtual base, but because B is not the most derived class and so doesn't get to construct its A.

When setting only C:A to virtual I get:

A() A(2) B(2) C(3) D()

Why do A's constructors both precede B's and C's? That behavior makes no sense to me.

Here C::A is the only virtual base class, so it gets initialized first. Again, since the constructor of D doesn't specify an initializer for A, that virtual subobject uses the default constructor.

Then the non-virtual base classes go in order. B comes before C, but B first needs to initialize its non-virtual A (with 2).

When setting only D:C to virtual I get:

A(3) C(3) A(2) B(2) D()

Why would C's constructor precede B's?

The only virtual base class is C, so it gets constructed before B. But this time the constructor of C must first initialize its non-virtual base subobject C::A. Then comes B, which must first initialize its non-virtual subobject B::A.

Finally, there's the "normal" pattern, which makes both A inheritances virtual, resulting in only one A subobject, which is the entire point of virtual inheritance. It's pointless to make B or C virtual unless you expect D to be reused as a base class in an even more complicated way.

#include <iostream>
using namespace std;

struct A{
A() { cout << "A()" << endl; }
A(int i) { cout << "A(" << i << ")" << endl; }
};

struct B : virtual public A{
B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
};

struct C : virtual public A{
C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
};

struct D : public B, public C{
D() : A(1), B(2), C(3){ cout << "D()" << endl; }
};

void main() {
D d;
system("pause");
}

Output:

A(1) B(2) C(3) D()

Note I've added an initializer A(1) to D, to show that a virtual subobject does not necessarily need to use the default constructor. You could have done this in any of your examples where at least one A inheritance is virtual.

(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.

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) { }
};

Default constructor getting called while using virtual inheritance

That's how virtual inheritance is described in the standard.

[12.6.2] — First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

In particular, when constructing C, the A subobject is initialized before anything else (including the B subobject). Since A is not in the mem-initializers list of the offending C constructor, the default constructor of A is used for that.

[12.6.2] — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

Then the B subobject is constructed.

[12.6.2] A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.

: A(z) in the constructor of B is ignored when constructing the B subobject of C.

In the everyday language this means you have to initialize a virtual base class in each direct or indirect derived class, as if it were a direct derived class. If you forget to do so, a default constructor will be forced, with potentially devastating consequences. (That's why you should strive to have either only the default constructor or no default constructor at all in any virtual base class).

Virtual inheritance impact on constructor

Firstly, since B derives from A non-virtually, D ends up with two A subobjects (as if you didn't use virtual inheritance at all).

The non-virtual A is constructed by B() (hence "A called from B"), and the virtual one prints "Constructor A" when constructed.

That's because the constructors of virtual (possibly indirect) bases are always called by the constructor of the most derived class (D), rather than by the constructors of any intermediate bases (C).

It means that : A("A called from C") in C() is ignored (since you're not constructing a standalone C). And since D() doesn't mention A in its member initializer list, the virtual A is constructed without any parameters (as if by : A()).


Also, it's worth mentioning that all virtual bases are constructed before the non-virtual ones.

So your code is guaranteed to print Constructor A before A called from B.



Related Topics



Leave a reply



Submit