22

The following code won't compile:

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

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

// most derived class
class C: public B {
public:
    C() {} // wrong!!!
};

If I call A's constructor in C's constructor initialization list, that is:

// most derived class
class C: public B {
public:
    C(): A(0) {} // OK!!!
};

it does work.

Apparently, the reason is because virtual base classes must always be constructed by the most derived classes.

I don't understand the reason behind this limitation.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
JFMR
  • 23,265
  • 4
  • 52
  • 76
  • 2
    Possible duplicate of [Why a rule to explicitly call a virtual base class constructor in initializer list of a most derived class, when an older ancestor already has it?](https://stackoverflow.com/questions/37976835/why-a-rule-to-explicitly-call-a-virtual-base-class-constructor-in-initializer-li) – Holt Jun 02 '17 at 08:53
  • 3
    Related: [c++ virtual inheritance](https://stackoverflow.com/q/2126522/514235) and [Understanding virtual base classes and constructor calls](https://stackoverflow.com/q/6461784/514235). @Holt, good finding. But I think the linked Qn by you was wrongly closed as duplicate of some less relevant Qn. I have reopened that and closed it as a duplicate of this Qn. In this thread we can expect better answer as it's more objective and latest. – iammilind Jun 02 '17 at 08:59

3 Answers3

29

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?
};
Oktalist
  • 14,336
  • 3
  • 43
  • 63
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • 18
    The famous diamond problem – CinCout Jun 02 '17 at 08:54
  • 1
    This would not be the only possible ambiguity with multiple inheritance, and the compiler could detect it equally well. So the argument is not convincing. – Peter - Reinstate Monica Jun 02 '17 at 14:02
  • 9
    @PeterA.Schneider The compiler could not detect it if one of the constructors were defined in a different translation unit. Also detecting is not the same as fixing. How would you fix the ambiguity in this example if you couldn't modify `B0` or `B1`? – Oktalist Jun 02 '17 at 14:37
  • 2
    @PeterA.Schneider let's suppose that you don't require C to define how it constructs A in some cases, where do you draw the line? when there is only one base? only when B0 and B1 both use constexpr values, that are the same value? only when B0 and B1 use the same literal? – Caleth Jun 02 '17 at 14:42
  • 1
    @Oktalist True on both accounts... I found the problem similar to ambiguous parent class function calls. But the functions are part of the class declaration, while the *implementation* of ctors are not, so right: The compiler can generally not see them. And it is true that there is no language construct to choose one of the initializations over the other (as opposed to a function call which is dis-ambiguated via scope resolution). – Peter - Reinstate Monica Jun 02 '17 at 14:43
8

Because in the class hierarchy having a virtually inherited base class, the base class would/may be shared by multiple classes (in diamond inheritance for example, where the same base class is inherited by multiple classes). It means, there would be only one copy of the virtually-inherited base class. It essentially means, the base class must be constructed first. It eventually means the derived class must instantiate the given base class.

For example:

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 // A is shared, and would have one copy only.
Ajay
  • 18,086
  • 12
  • 59
  • 105
  • The shared base class object (of type A here) must actually be created last (before the most derived object, that is, but after its other base class objects of type B1 and B2). – Peter - Reinstate Monica Jun 02 '17 at 14:04
5

I find this rule error-prone and cumbersome (but then, what part of multiple inheritance isn't?).

But the logically imposed order of construction must differ from the case of normal (non-virtual) inheritance. Consider Ajay's example, minus virtual:

class A;
class B1 : A;
class B2 : A;
class C: B1,B2

In this case for each C two As are constructed, one as part of B1, the other one as part of B2. The code of the B classes is responsible for that, and can do it. The order of events is:

Start C ctor
   Start B1 ctor
      A ctor (in B's ctor code)
   End B1 ctor
   Start B2 ctor
      A ctor (in B's ctor code)
   End B2 ctor
End C ctor

Now consider virtual inheritance in

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 

One order of event is

Start C ctor
   A ctor // not B's code!
   Start B1 ctor
      // NO A ctor
   End B1 ctor
   Start B2 ctor
      // NO A ctor
   End B2 ctor
End C ctor

The important logical distinction is that the virtually inherited base class sub-object of type A is part of the most derived class and under the control of it (here C).

B's constructors know nothing about and cannot access A. Consequently they cannot construct sub-objects of A, including base classes.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • 1
    Here, A is not invoked either from B1 or B2, but from C, but as you mentioned, if A is constructor after B1 and B2 and if constructors of B1 and B2 uses any variables of A, what would be the situation?. I think A has to be constructed first followed by the remaining. Kindly clarify – infinite loop Aug 15 '17 at 15:09
  • 1
    @infiniteloop Sounds reasonable. It would indeed mean that the virtual base must be constructed before any of the derived members. – Peter - Reinstate Monica Aug 18 '17 at 00:50