Virtual Inheritance: Error: no unique final overrider
The most-derived class has to provide an implementation of the virtual functions in the virtual base class - otherwise how would it provide that base class interface, given the intermediate classes (i.e. your der1
and der2
) provide two alternatives already - which one should it call? You have to disambiguate the situation (i.e. with der3::fun()
).
Sure you're not actually calling der3::fun()
as you're explicitly requesting base::fun()
, but that doesn't mean the rules don't apply, any more than thinking you could instantiate an abstract class if you don't try to call the pure-virtual functions.... The program is ill-formed until the code ties off these loose ends.
Why does GCC give me an error: no unique final overrider?
It's because C
inherits in a non-virtual way from B
while D
inherits in a virtual way from B
. This gives you B
two times including two f()
.
Try virtual inheritance of B
in C
.
Update: So why does it work when you remove the virtual inheritance in B
from A
? Because it changes the "final overrider". Without virtual in B
from A
and in C
from B
you have A
two times: once in C
(with the final override of f()
in B
) and once in the virtual B
in D
(with the final override of f()
in B
). If you add back the virtual inheritance in B
to A
, A
will be present only once and there will be two final overrides competing to implement the pure f()
from A
, both in B
, once from C
and once from the virtual B
.
As a workaround you could add a using
to D, that is using C::f;
or using B::f
.
See C++ 10.3/2
How to add final overrider
Keyword final
in virtual method declaration prevents multiple inheritance, so if I tried to solve ambiguity in that case, it's a wrong approach. If both Box and Sphere would have final word, you'll get error "virtual function 'Shape::print' has more than one final overrider in 'GeoDisc'". The legal ambiguity solution would be:
struct Sphere : public virtual Shape
{
void print() override
{
std::cout << "SPHERE" << std::endl;
}
};
struct GeoDisc : public Box, public Sphere
{
void print() final override
{
Sphere::print();
}
};
Virtual multiple inheritance - final overrider
In the dreaded diamond there is a single base, from which the two intermediate objects derive and then the fourth type closes the diamond with multiple inheritance from both types in the intermediate levels.
Your question seems to be how many f
functions are declared in the previous example? and the answer is one.
Lets start with the simpler example of a linear hierarchy of just base and derived:
struct base {
virtual void f() {}
};
struct derived : base {
virtual void f() {}
};
In this example there is a single f
declared for which there are two overrides, base::f
and derived::f
. In an object of type derived
, the final overrider is derived::f
. It is important to note that both f
functions represent a single function that has multiple implementations.
Now, going back to the original example, on the line on the right, Base::f
and Right::f
are in the same way the same function that is overridden. So for an object of type Right
, the final overrider is Right::f
. Now for a final object of type Left
, the final overrider is Base::f
as Left
does not override the function.
When the diamond is closed, and because inheritance is virtual
there is a single Base
object, that declares a single f
function. In the second level of inheritance, Right
overrides that function with its own implementation and that is the final overrider for the most derived type Bottom
.
You might want to look at this outside of the standard and take a look at how this is actually implemented by compilers. The compiler, when creating the Base
object it adds a hidden pointer vptr
to the virtual table. The virtual table holds pointers to thunks (for simplicity just assume that the table held pointers to the function's final overriders, [1]). In this case, the Base
object will contain no member data and just a pointer to a table that holds a pointer to the function Base::f
.
When Left
extends Base
, a new vtable is created for Left
and the pointer in that vtable is set to the final overrider of f
at this level, which is incidentally Base::f
so the pointers in both vtables (ignoring the trampolin) jump to the same actual implementation. When an object of type Left
is being constructed, the Base
subobject is initialized first, and then prior to initialization of the members of Left
(if there were) the Base::vptr
pointer is updated to refer to Left::vtable
(i.e. the pointer stored in Base
refers to the table defined for Left
).
On the other side of the diamond, the vtable that is created for Right
contains a single thunk that ends up calling Right::f
. If an object of type Right
was to be created the same initialization process would happen and the Base::vptr
would point to Derived::f
.
Now we get to the final object Bottom
. Again, a vtable is generated for the type Bottom
and that vtable, as is the case in all others, contains a single entry that represents f
. The compiler analyzes the hierarchy of inheritance and determines that Right::f
overrides Base::f
, and there is no equivalent override on the left branch, so in Bottom
's vtable the pointer representing f
refers to Right::f
. Again, during construction of the Bottom
object, the Base::vptr
is updated to refer to Bottom
's vtable.
As you see, all four vtables have a single entry for f
, there is a single f
in the program, even if the value stored in each vtable is different (the final overriders differ).
[1] The thunk is a small piece of code that adapts the this
pointer if needed (multiple inheritance usually implies it is needed) and then forwards the call to the actual override. In the event of single inheritance, the this
pointer does not need to be updated and the thunk disappears, with the entry in the vtable pointing directly to the actual function.
Is this example working with virtual inheritance in C++?
You need virtual inheritance in this way:
struct A {
virtual int f() = 0;
};
struct B : virtual A {
int f() { return 1; }
};
struct C : virtual A {};
struct D : B, C {};
int main() {
D d;
return d.f();
}
In the dupe I commented you can see this relation
A
/ \
B C
\ /
D
for virtual inheritance and
A A
| |
B C
\ /
D
without virtual inheritance.
In the second case D
contains two function definitions. One is implemented and one isn't.
Virtual methods on a virtual base class
It should not compile, the function foo will be ambiguous. Since A::foo() is pure virtual function, the ambiguity has to be resolved.
Virtual inheritance ambiguous function
The problem is with C
not with your expression call. As f
is virtual and redefined twice in B1
and B2
then the C
class is malformed because a call to f
would be ambiguous (which override to choose?). Add an override of f
in C
and everything will be ok.
Diamond issue when trying to resolve it using virtual inheritance
Think as a compiler: what method should you call ?
You have a triangle that is both a Square and a Rectangle and you are asking is area. Should your triangle choose to use Rectangle.getArea()
or Square.getArea()
?
The compiler cannot know. A solution here is to overide the getArea() method, for instance:
class Triangle: public Rectangle, Square
{
public:
void blabla(){}
void getArea()
{
Square::getArea; //I'm a square
Rectange::getArea; //I'm a rectangle
}
};
This problem would be trigger even is there was no class Base. An exemple on use of diamond inheritance would look like this:
class Base
{
protected:
int x;
};
class Derived1: virtual public Base
{
//some stuff
};
class Derived2: virtual public Base
{
//some stuff
};
class Join: public Derived1, public Derived2
{
int getX(){
return x;
}
};
In here, using virtual inheritance allows us to only have one instance of the Base class in the Join instance, instead of 2 without and an error on which x should be selected.
Related Topics
Difference Between Invalidaterect and Redrawwindow
Qt "Private Slots:" What Is This
Setting Width in C++ Output Stream
Lvalue to Rvalue Reference Binding
How to Write a Variadic Template Recursive Function
Access Extern Variable in C++ from Another File
Why Can't You Do Bitwise Operations on Pointer in C, and Is There a Way Around This
How to Perform a Pairwise Binary Operation Between the Elements of Two Containers
C++ Specialization of Template Function Inside Template Class
Print MACro Values Without Knowing the Amount of MACros
What Optimization Does Move Semantics Provide If We Already Have Rvo
How to Disable Exceptions in Stl
Opencv Read Jpeg Image from Buffer
Direct Boost Serialization to Char Array
Is the Std::Set Iteration Order Always Ascending According to the C++ Specification