I have the following class hierarchy with a virtual GrandParent
and non-virtual Parent
and Child
:
class GrandParent {
protected:
explicit GrandParent(const float &max_dur);
virtual ~GrandParent() {}
private:
const float _max_dur;
};
class Parent : public virtual GrandParent {
public:
explicit Parent(const float &max_dur = 0);
};
class Child : public Parent {
public:
explicit Child(const float &max_dur = 0);
};
Their constructors are nested like so:
// GrandParent constructor
GrandParent::GrandParent(const float &max_dur)
: _max_dur{max_dur} {}
// Use GrandParent constructor
Parent::Parent(const float &max_dur)
: GrandParent{max_dur} {}
// Use Parent constructor
Child::Child(const float &max_dur)
: Parent{max_dur} {} // <- error occurs here
When I build, I get the following error message:
error: no matching function for call to ‘GrandParent::GrandParent()’
It seems as if the Child
constructor is ignored and it jumps to GrandParent
instead. Modifying the Child
constructor to directly call the GrandParent
constructor (thus skipping a generation), I can bypass the error but it seems like the wrong approach.
Thanks in advance for your help!
Solution
Fixed by following 463035818-is-not-a-number's answer to call the GrandParent
's constructor explicitly and 康桓瑋's suggestion in order to call the Parent
's constructor as well:
Child::Child(const float &max_dur)
: GrandParent{max_dur}, Parent{max_dur} {}
CodePudding user response:
From the faq:
What special considerations do I need to know about when I inherit from a class that uses virtual inheritance?
Initialization list of most-derived-class’s ctor directly invokes the virtual base class’s ctor.
Because a virtual base class subobject occurs only once in an instance, there are special rules to make sure the virtual base class’s constructor and destructor get called exactly once per instance. The C rules say that virtual base classes are constructed before all non-virtual base classes. The thing you as a programmer need to know is this: constructors for virtual base classes anywhere in your class’s inheritance hierarchy are called by the “most derived” class’s constructor.
The constructor of Child
calls the constructor of GrandParent
directly, because GrandParent
is a virtual base. And because you did not call it explicitly, the default constuctor is called, but GrandParent
has no default constructor.
Modifying the Child constructor to directly call the GrandParent constructor (thus skipping a generation), I can bypass the error but it seems like the wrong approach.
This is exactly the right approach. Child
s constructor does call GrandParent
s constructor, you cannot do anything about that when GrandParent
is a virtual base and Child
is the most-derived-class. What you can do is: Choose the right constructor instead of letting the compiler try to call the non-existent default constructor.