7

Why does this print 20000? Code explicitly calls specific base constructors all the way up the inheritance train, yet ignores specified constructor and uses the default constructor instead.

#include <iostream>

struct Car
{
  Car() : price(20000) {}
  Car(double b) : price(b*1.1) {}
  double price;
};

struct Toyota : public virtual Car
{
  Toyota(double b) : Car(b) {}
};

struct Prius : public Toyota
{
  Prius(double b) : Toyota(b)  {}
};

int main(int argc, char** argv)
{
  Prius p(30000);

  std::cout << p.price << std::endl;

  return 0;
}
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
code
  • 1,056
  • 10
  • 22
  • See also [In C++, what is a virtual base class?](https://stackoverflow.com/questions/21558/in-c-what-is-a-virtual-base-class). as well as [Why must virtual base classes be constructed by the most derived class?](https://stackoverflow.com/questions/44324583/why-must-virtual-base-classes-be-constructed-by-the-most-derived-class) and [c++ virtual inheritance](https://stackoverflow.com/questions/2126522/c-virtual-inheritance). – Richard Chambers Apr 26 '18 at 18:42
  • It is often recommended to use virtual base for interfaces only. Not castrated Java interfaces, but class without observable state, often without data members. These classes rarely have meaningful non default ctors (they might have a do nothing copy ctor). – curiousguy May 11 '18 at 17:30

1 Answers1

10

Virtual base class must be constructed by the most-derived class; that's the only way that makes sense considering the possibility of a diamond-shaped hierarchy.

In your case, Prius constructs Car using its default constructor. If you want the other constructor, you'd have to call it explicitly, as in

Prius(double b) : Car(b), Toyota(b) {}
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Thanks Igor, could you please post modified code to be completely clear. – code Apr 26 '18 at 18:17
  • How 'bout now?. – Igor Tandetnik Apr 26 '18 at 18:19
  • "_that's the only way that makes sense considering the possibility of a diamond-shaped hierarchy_" No it's not. Consider this claim: "Virtual base class pure virtual functions must be overridden in the most-derived class; that's the only way that makes sense considering the possibility of a diamond-shaped hierarchy". – curiousguy May 11 '18 at 16:55
  • @curiousguy Perhaps that's not the only way, but a) no other occurs to me (possibly due to lack of imagination), and anyway b) that's how the C++ standard defines the language should work, for better or worse. – Igor Tandetnik May 11 '18 at 17:03
  • Re: your example with virtual functions. A derived class may choose to override or not override the base class' virtual function, so it's possible to construct a diamond-shaped hierarchy were overrides don't in fact conflict. When they do conflict, the most derived class does have to override. In contrast, a derived class can't choose not to call a base class' constructor, so in essence, constructor calls always conflict, and so the most-derived class is required to step in. – Igor Tandetnik May 11 '18 at 17:04
  • Only because C++ defines not naming a base class in a ctor-init-list as calling the default ctor. Calling the default ctor of a class object is the default in C++: this is default behavior (default behavior: not overriding the built-in semantic of class object, which is init by default ctor unless parameters are passed to the ctor) for members of class type and for base classes. – curiousguy May 11 '18 at 17:26
  • For virtual bases, this "default" is overriding the behavior specified in base class ctors, so it isn't default. There is no "I pass, do the default" syntax in the ctor-init-list to allow a previous (in inheritance order) ctor-init-list. – curiousguy May 11 '18 at 17:28