I am trying to achieve the following design, which is a dreaded diamond situation:
struct super_base
{
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() {}
Which doesn't work. The error for the above code using Clang is quite good at explaining the mistake:
error: call to implicitly-deleted default constructor of 'base'
derived(int a) : super_base{a} {}
^
note: default constructor of 'base' is implicitly deleted because base
class 'super_base' has no default constructor
So I added an empty constructor for super_base
, and it works:
#include<iostream>
#include<stdexcept>
struct super_base
{
super_base() { throw std::logic_error{"Constructor should not be called."}; };
super_base(int a) { b = a; }
int b;
};
struct base : virtual super_base
{};
struct other_base : virtual super_base
{};
struct derived : base, other_base
{
derived(int a) : super_base{a} {}
};
int main() { std::cout << derived(10).b << '\n'; }
But why does this not throw ?
P.S.: I purposefully used a dreaded diamond pattern to illustrate the use of virtual inheritance. The problem remains the same with single inheritance.
P.P.S.: The compiler used is Clang 3.9.1. The results are the same with GCC 6.3.1.
struct base:super_base {}:
this tries to create some constructors. One of them it tries to create is base::base()
.
If super_base
has no super_base::super_base()
, this constructor is deleted.
If we have super_base::super_base()=default
or {}
, then base::base()
by default exists, even if you don't =default
it.
The same thing happens in other_base
.
And your derived class tries to call the base object constructors, which are deleted, which gives you an error.
Now, why isn't it called? When you have a virtual base class, the constructor is only called once. Intermediate types which call the virtual base classes constructor have their calls ignored.
So we have derived()
calling base()
, base()
calling super_base()
, but that call is ignored because of virtual inheritance.
derived()
'd call of super_base(int)
is used instead.
Now, why are these the rules? Because C++ doesn't have the concept of "constructor that can only be called if you are a derived class of this class explicitly calling a specific base constructor". Virtual inheritance is not quite as full featured as you might like.