Suppose a pure abstract base class might be inherited by multiple paths in a dreaded diamond of death. Is there any good reason to use virtual inheritance, or doesn't it matter?
There is a good reason to use virtual inheritance, because the compiler will enforce it. Consider this scenario:
struct A {
virtual ~A() = default;
virtual void f() = 0;
};
struct B : A {};
struct C : A {};
struct D : B, C {
void f() override {}
};
Without virtual inheritance, the following code won't compile
D d;
A &a = d; // error: 'A' is an ambiguous base of 'D'
When you change B and C such that
struct B : virtual A {};
struct C : virtual A {};
everything will build just fine.
Related
Could I do this?
class A {
public:
virtual void aFoo() = 0;
};
class B : virtual public A {
public:
virtual void aFoo() { ... }
};
class D : public A {};
class C : public B, virtual public D {};
The issue is with the implementation of aFoo() in B and the lack of it in C. When it comes to compiling I see this error:
error: no unique final overrider for ‘virtual void A::aFoo()’ in ‘C’
Shouldn't it be possible to override a pure virtual method of a virtual base class in B?
Just edited the example to match the actual use case. Now looking at it in this simplified way I am not quite sure if this is even possible, let alone good design.
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.
Still learning basics of C++
I have a very simple query regarding the virtual keyword in multiple inheritance.
I have the following code:
class A {};
class B : public A {};
//1. class C : public A, public B {};
//2. class C : virtual public A, public B {};
//3. class C : public A, virtual public B {};
//4. class C : virtual public A, virtual public B {};
int main() {
C f;
return 0;
}
Now I know (by actually trying this code out), that 1 and 3, both cases where we do not use virtual keyword while inheriting class A work.
I understand it is the Diamond Problem of multiple inheritance, and the compiler error goes away when I inherit class A as virtual in class B:
class B: virtual public A
But I wanted to experiment with the code and removed it.
My question is, Why does creation of an object of class C fail when we inherit class A using virtual?
Note: I could not find a good article to explain in depth what this virtual keyword means and how the compiler reads it and what does the compiler do after reading the Virtual Keyword.
EDIT:
This is an experiment that I am doing, so no practical use (to me personally).
When do this:
class A {};
class B : public A {};
class C : virtual public A, virtual public B {};
int main() {
C f;
return 0;
}
Compiler throws error: error C2584: 'C': direct base 'A' is inaccessible; already a base of 'B'
even when I don't use virtual, compiler still gives the error msg as a "Warning"
I know there are many questions out there concerning MI, however, none seemed to answer my question. I have the following minimal example:
#include <iostream>
struct Base{
virtual void foo() = 0;
};
struct A : public virtual Base{
void foo(){
std::cout << "A::foo()" << std::endl;
}
void foo( int a){
std::cout << "A::foo(int a)" << std::endl;
}
};
struct B : public virtual Base{
virtual void foo( int a ) = 0;
};
struct C : public B,public A{
using A::foo;
};
int main( int argc, char* argv[]){
C c;
c.foo();
c.foo( 1 );
}
where Base, and B are entirely virtual classes and A provides all the implementation. However, the code does not compile but instead gives me the following error message
mh.cpp: In function ‘int main(int, char**)’:
mh.cpp:22:11: error: cannot declare variable ‘c’ to be of abstract type ‘C’
C c;
^
mh.cpp:17:12: note: because the following virtual functions are pure within ‘C’:
struct C : public B,public A{
^
mh.cpp:15:22: note: virtual void B::foo(int)
virtual void foo( int a ) = 0;
The behaviour I desire can be achieved by extending class C to
struct C : public B,public A{
using A::foo;
void foo( int a ){
A::foo( a );
}
};
However, I'd prefer not adding this redundant method. Is there some way I can achieve that result?
A::foo(int) can't override B::foo(int) unless A derives from B.
So, if you don't want to create the forwarding override in C, your alternatives are:
move the foo(int) pure virtual overload into Base
make A derive from B
restructure your interfaces so you don't need diamond-shaped virtual inheritance in the first place
No, if you inherit from a pure virtual class and you want to use this derived class (declare object of that type, not a pointer), you must implement all its pure virtual methods.
In the same manner you could ask the following:
class A
{
public:
virtual void foo()=0;
virtual void hello()=0;
}
class B: public A
{
public:
void myFoo();
void hello() { std::cout << "Hello!"; }
}
In class B, I don't want to use A::foo() but only B::myFoo() what should I do?
In such case you either need to provide some dummy implementation for the pure virtual function or review again your design... probably you should not inherit from A.
Similar thing is in your case. Probably your class C should not inherit from class B.
Just replace your single line as below.
Derived C only from A.
Your code
struct C : public B,public A{
using A::foo;
};
Replace above code with below code,
struct C : public A{
using A::foo;
};
Given the following code (without virtual inheritance) :
class A
{
public:
virtual void f() = 0;
};
class B : public A
{
public:
virtual void f() {}
};
class C : public A
{
public:
virtual void f() {}
};
class D : public B, public C
{
/* some code */
};
int main()
{
D d;
return 0;
}
the code compile.
On the other hand , here :
class A
{
public:
virtual void f() = 0;
};
class B : virtual public A
{
virtual void f() {}
};
class C : virtual public A
{
virtual void f() {}
};
class D : public B, public C
{
/* some code */
};
int main()
{
D d;
return 0;
}
The compiler presents a compilation error:
no unique final overrider for 'virtual void A::f()' in 'D' .
Why is it different in the second code ?
Your first scenario hierarchy corresponds to:
F() F()
A A
| |
F() B C F()
\ /
D
Where D is not abstract, because there are two A subobjects in an object of type D: One that is made concrete by B through the lattice of B, and another that is made concrete through the lattice of C.
Unless you try to invoke the function F() on object of D there will not be any ambiguity.
Your second scenario hierarchy corresponds to:
F()
A
/ \
F() B C F()
\ /
D
In this scenario, the object D has a single Base class A sub object, and it must override and provide implementation of the pure virtual function in that subobject.
Herb Sutter's articles in Guru Of The Week(GOTW) are a nice read for Multiple Inheritance:
Multiple Inheritance Part I
Multiple Inheritance Part II
Multiple Inheritance Part III
With the virtual inheritance a D object has a single base-class A sub-object. This single sub-object can’t have two different implementations of a virtual function. In contrast, without virtual inheritance a D object has two distinct base-class A sub-objects, each with its own implementation of the function (which is OK until you try to call it on a D object, at which point you need to indicate which one you want).
Cheers & hth.
In the code below, I get the following warning and error:
test.cpp:15: warning: direct base 'B' inaccessible in 'D' due to ambiguity
test.cpp:15: error: no unique final overrider for 'virtual void A::f()' in 'D'
But if I remove the virtual inheritance of B from A (i.e. struct B : public A), I only get the warning, no error.
struct A
{
virtual void f() = 0;
};
struct B : public virtual A
{
void f() {}
};
class C : public B
{};
struct D : public C, virtual B
{};
int main()
{
return 0;
}
Why? Is this the dreaded diamond?
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
Let's look at the definition of 'final overrider' from 10.3[class.virtual]/2
A virtual member function C::vf of a class object S is a final overrider unless the most derived class of which S is a base class subobject (if any) declares or inherits another member function that overrides vf.
In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.
With virtual inheritance from A, there is only one base class subobject of type A, and its virtual member function f() has more than one final overrider (one in each subobject of type B)
Without virtual inheritance from A, there are two different base class subobjects of type A, and their virtual member functions f() each has its own final overrider (one in each B subobject)
Virtual base sub-objects are 'shared' between all base sub-objects in a complete object. Since A is is shared between D::C::B and D::B it can't tell which B object should have its f() called as the override for A::f().
Consider:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : virtual A
{
void f() { std::cout << "B\n"; }
};
struct C : virtual A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
A *a = dynamic_cast<A*>(&d); // single shared A between B and C
a->f(); // Should B::f() be called, or C::f()?
}
The B and C base sub-objects in D both share the same A base sub-object. When we call A::f() a virtual look-up is done for the overriding function. But both B and C are trying to override it, so which one 'wins'? Does x->f() print "B" or "C"? The answer is that a program that gets into the situation is ill-formed.
When we eliminate the sharing by making B and C inherit non-virtually, then the separate A base sub-objects each have their functions overridden by unique base classes:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : A
{
void f() { std::cout << "B\n"; }
};
struct C : A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
// two different A objects
A *a1 = static_cast<A*>(static_cast<B*>(&d));
A *a2 = static_cast<A*>(static_cast<C*>(&d));
a1->f();
a2->f();
}