Is it possible to have a number of partial implementations of an abstract interface, and then collect these partial implementations into a single concrete class by using multiple inheritence?
Yes.
Each Base
base class subobject brings two pure virtual functions. How many of those base subobjects do you want in Deriv
?
- If you want 2
Base
base class subobject, Deriv::D1::Base
and Deriv::D2::Base
(so conversions from Deriv&
to Base&
would be ambiguous) then you will have 4 different virtual functions in Deriv
: Deriv::D1::Base::F1()
, Deriv::D1::Base::F2()
, Deriv::D2::Base::F1()
, Deriv::D2::Base::F2()
. Only the first and last ones are implemented, so the two middle ones are pure virtual: Deriv::D1::Base::F2()
, Deriv::D2::Base::F1()
. You have two entirely independent inheritance relations: Deriv
inherits from D1
and Deriv
inherits from D2
.
- If you want only one
Base
base class subobject Deriv::Base
, then you will have only 2 different virtual functions in Deriv
: Base::F1()
, Base::F2()
.
Non-virtual inheritance in C++ is "concrete" inheritance, like containment: struct D : B
means that for each D
object there is exactly one B
base class subobject, just like struct C { M m; }
means that for each C
there is exactly one M
class member subobject. These relations are one-to-one.
OTOH, virtual inheritance is a more "abstract": struct D : virtual B
means that for each D
object is associated with a B
base class subobject, but this relation is many-to-one, like struct C { M &m; }
.
In general, for any derived class D
(here Deriv
), and for any virtual base B
of D
(here Base
), all the base classes of D
virtually derived from B
(here Deriv::D1
, Deriv::D2
) contribute to the overriding of the virtual functions in the base class:
Base::F1()
is overridden by Deriv::D1::F1()
Base::F2()
is overridden by Deriv::D2::F2()
Non-virtual and virtual inheritance are very different inheritance relations, just like if non-virtual member functions and a virtual function introduce different relations between functions with the same signature in derived and base classes (hiding vs. overriding).
Like virtual and non-virtual functions, virtual and non-virtual base classes must be used in different situations (and one is not "better" than the other in general).
How to "fix" your code without virtual inheritance?
struct Deriv : D1, D2
{
using D1::F1; // I added these using clauses when it first didn't compile - they don't help
using D2::F2;
};
Yes: using declaration control name-lookup, so they affect visibility and ambiguity issues, not virtual functions overriding.
With your original non-virtual inheritance based design, in order to make Deriv
a concrete class, you would have to explicitly implement both F1()
and F2()
virtual function signatures (there are 4 virtual functions in Deriv
, but with only 2 different signatures), so you need 2 function definitions:
struct Deriv : D1, D2
{
void F1() override { D1::F1(); }
void F2() override { D2::F2(); }
};
Note that Deriv::F1()
overrides Deriv::D1::F1()
and Deriv::D2::F1()
.