Reputation: 730
class Base
{
public:
Base(std::string arg1, std::string arg2) : arg1(arg1), arg2(arg2) {}
string arg1,arg2;
virtual void f() = 0;
inline virtual ~Base() {}
};
class Mixin1 : virtual public Base
{
};
//Virtual class uses virtual base and a mixin
class VirtualDerived : virtual public Base, public Mixin1
{
public:
VirtualDerived(string arg2) : Base("some literal", arg2) {}
};
//Here I am concretely subclassing VirtualDerived, whose constructor constructs all virtual superclasses.
class ConcreteDerived : public VirtualDerived
{
public:
ConcreteDerived(string arg2) : VirtualDerived(arg2) {}
inline void f() {}
};
int main(int argc, const char * argv[]) {
ConcreteDerived cd("hello");
return 0;
}
I am deriving from VirtualDerived, whose constructor will initialize Base. Whether I explicitly declare VirtualDerived's inheritance of Mixin1 virtual or not makes no difference.
The error I get is that ConcreteDerived must explicitly call Base's constructor.
Making ConcreteDerived's inheritance virtual similarly makes no difference.
The problem is not with VirutalDerived's constructor forwarding. If I implement f to make it a concerete class, I can create it without issue:
class VirtualDerived : virtual public Base, public Mixin1
{
public:
VirtualDerived(string arg2) : Base("some literal", arg2) {}
inline void f(){}
};
int main(int argc, const char * argv[]) {
VirtualDerived cd("hello");
return 0;
}
The compiler seems to understand that VirtualDerived's constructor calls Base's non-trivial constructor when VirtualDerived is created directly. But not if something derives VirtualDerived and calls it. Why is this the case?
Upvotes: 2
Views: 231
Reputation: 16824
The thing to understand about virtual inheritance is that when you virtually inherit from a class, it goes to the "front" of the inheritance "queue"; that is, you must initialise virtual bases before all other base classes.
In this case, ConcreteDerived
has Base
as an indirect virtual base, via VirtualDerived
. Since Base
has no default constructor, you must specify it in the initialiser list of ConcreteDerived
.
The reasoning is this: imagine you had another intermediate class such as
class VirtualDerived2 : virtual public Base
{
public:
VirtualDerived2() : Base("arg1", "arg2") {}
};
Here VirtualDerived2
also virtually inherits from Base
, and initialises it with its own set of arguments. Now we add another concrete class:
class ConcreteDerived2 : public VirtualDerived, public VirtualDerived2
{
// ...
};
Now ConcreteDerived2
has two base classes, both of which virtually inherit from Base
. But you only have one copy of Base
in the hierarchy -- that's the entire point of virtual inheritance. So now which parameters are you going to use to initialise your (single) copy of Base
? Those from VirtualDerived
, or those from VirtualDerived2
? There is no good answer for this, so C++ makes you choose in the most derived class.
Upvotes: 2