Why is there ambiguity in this diamond pattern? - c++

#include <iostream>
using namespace std;
class A { public: void eat(){ cout<<"A";} };
class B: public A { public: void eat(){ cout<<"B";} };
class C: public A { public: void eat(){ cout<<"C";} };
class D: public B,C { public: void eat(){ cout<<"D";} };
int main(){
A *a = new D();
a->eat();
}
I am not sure this is called diamond problem or not, but why doesn't this work?
I have given the defination for eat() for D. So, it doesn't need to use either B's or C's copy (so, there should be no problem).
When I said, a->eat() (remember eat() is not virtual), there is only one possible eat() to call, that of A.
Why then, do I get this error:
'A' is an ambiguous base of 'D'
What exactly does A *a = new D(); mean to the compiler??
and
Why does the same problem not occur when I use D *d = new D();?

The diamond results in TWO instances of A in the D object, and it is ambiguous which one you are referring to - you need to use virtual inheritance to solve this:
class B: virtual public A { public: void eat(){ cout<<"B";} };
class C: virtual public A { public: void eat(){ cout<<"C";} };
assuming that you actually only wanted one instance. I also assume you really meant:
class D: public B, public C { public: void eat(){ cout<<"D";} };

Imagine a slightly different scenario
class A { protected: int a; public: void eat(){ a++; cout<<a;} };
class B: public A { public: void eat(){ cout<<a;} };
class C: public A { public: void eat(){ cout<<a;} };
class D: public B,C { public: void eat(){ cout<<"D";} };
int main(){
A *a = new D();
a->eat();
}
If this would work, would it increment the a in B or the a in C? That's why it's ambiguous. The this pointer and any non-static data member is distinct for the two A subobjects (one of which is contained by the B subobject, and the other by the C subobject). Try changing your code like this and it will work (in that it compiles and prints "A")
class A { public: void eat(){ cout<<"A";} };
class B: public A { public: void eat(){ cout<<"B";} };
class C: public A { public: void eat(){ cout<<"C";} };
class D: public B, public C { public: void eat(){ cout<<"D";} };
int main(){
A *a = static_cast<B*>(new D());
// A *a = static_cast<C*>(new D());
a->eat();
}
That will call eat on the A subobject of B and C respectively.

Note that the compile error is on the "A *a = new D();" line, not on the call to "eat".
The problem is that because you used non-virtual inheritance, you end up with class A twice: once through B, and once through C. If for example you add a member m to A, then D has two of them: B::m, and C::m.
Sometimes, you really want to have A twice in the derivation graph, in which case you always need to indicate which A you are talking about. In D, you would be able to reference B::m and C::m separately.
Sometimes, though, you really want only one A, in which case you need to use virtual inheritance.

For a truly unusual situation, Neil's answer is actually wrong (at least partly).
With out virtual inheritance, you get two separate copies of A in the final object.
"The diamond" results in a single copy of A in the final object, and is produced by using virtual inheritance:
Since "the diamond" means there's only one copy of A in the final object, a reference to A produces no ambiguity. Without virtual inheritance, a reference to A could refer to either of two different objects (the one on the left or the one on the right in the diagram).

The error you're getting isn't coming from calling eat() - it's coming from the line before. It's the upcast itself that creates the ambiguity. As Neil Butterworth points out, there are two copies of A in your D, and the compiler doesn't know which one you want a to point at.

You want: (Achievable with virtual inheritance)
D
/ \
B C
\ /
A
And not: (What happens without virtual inheritance)
D
/ \
B C
| |
A A
Virtual inheritance means that there will be only 1 instance of the base A class not 2.
Your type D would have 2 vtable pointers (you can see them in the first diagram), one for B and one for C who virtually inherit A. D's object size is increased because it stores 2 pointers now; however there is only one A now.
So B::A and C::A are the same and so there can be no ambiguous calls from D. If you don't use virtual inheritance you have the second diagram above. And any call to a member of A then becomes ambiguous and you need to specify which path you want to take.
Wikipedia has another good rundown and example here

Related

Is it possible to create a pointer to an inherited class by incrementing a base class pointer(C++)?

Based on what I’ve read, it seems the memory for derived objects is made sequentially with the base class and all its data made first and then immediately followed by the following classes down the inheritance tree. So if I make a base class pointer that is equal to a new derived class object, and then increment it by one(which will actually add the size of the base class to the address), then will I arrive at the derived class? If so, can I then access the derived class’s data in this way?
Yes, and no. In the very simplest case it will work in most cases:
class Base {
public:
int v;
};
class Derived : public Base {
public:
int b;
};
int main() {
Derived d;
Base* p = &d;
p++;
// these will match on all compilers I'm aware of
printf("%p %p\n", p, &d.b);
return 0;
}
For single inheritance, that is typically what you'll see from most compilers (although I'd be very worried actually relying on that in production code!)
However, sadly things aren't always that simple! In C++ we often have multiple inheritance, virtual inheritance, abstract base classes and all those bits of goodness. So here is a scenario where it would absolutely not work!
struct Animal {
virtual ~Animal() = default;
virtual void Eat() {}
int a;
};
struct Mammal: Animal {
virtual void Breathe() {}
int b;
};
struct WingedAnimal: Animal {
virtual void Flap() {}
int c;
};
// A bat is a winged mammal
struct Bat: Mammal, WingedAnimal {
int d;
};
There are however far safer approaches to handling upcasting (e.g. dynamic_cast, or your own RTTI system). You probably will want to be using those :)

store abstract member, keep interface simple

I know that it is not possible to have an instance of an abstract class as a base member of another class, i.e.,
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base & b): b_(b) {};
virtual ~D() {};
private:
Base b_;
}
int main() {
C1 c;
D d(c);
}
will fail to compile with the error
test.cpp:22:10: error: cannot declare field ‘D::b_’ to be of abstract type ‘Base’
The obvious workaround is to use (shared) pointers instead. This, however, makes main somewhat more complicated to read,
int main() {
auto c = std::make_shared<C1>();
D d(c);
}
which I would really like to avoid.
Is there a way to keep the main file as simple as in the above example and still achieve the desired functionality?
You can't. When you are creating D it allocates (in heap or in stack) memory for B. And C1 class needs size of base class B plus size of extra variables/etc in C1 itself even if there are nothing new.
So, use pointers instead.
The error caused by virtual int yield() = 0;. If you use virtual int yield(), it will works. When you used virtual int yield() = 0;, it said that the function is a pure virtual function, so you must override it. So you should give its inheritance class and use the instance of inheritance class in class C1. In a world, virtual int yield() = 0; only remind you that it is only a interface, you must override it. I hope this can help you.
Since Base is an abstract class (has at least one pure virtual function), it can't be instantiated directly.
When you declare D's class member as "Base b_", you are effectively trying to create an instance. You can instead use a pointer there (or some kind of safe/smart pointer).
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base * b): b_(b) {};
virtual ~D() {};
private:
Base *b_; // Use a pointer or safe ptr or something of that sort.
}
int main() {
C1 c;
D d(&c);
}
No. One of the properties of an abstract class is that it cannot be instantiated. That means an instance of an abstract class cannot be a member of another class.
Even if Base was not abstract, your class D's constructor would be slicing the object passed. If passed an instance of C1, the copying (in the initialiser list of D's constructor) would not magically cause an instance of D to contain an object of type C. It would instead create a copy only of the Base part of that object.
In short, your design is broken, and will not work even if - syntactically - it would be possible to simplify the code in main().

C++ pseudo-diamond [duplicate]

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.

virtual inheritance and signature overlapping

Let's say we have following code:
struct A{
virtual ~A(){}
void f(){
p = 42;
}
int p;
};
struct B : public virtual A{};
struct C : public virtual A{};
struct D : public B, public C, public A{}; //let's add non-virtual inheritance
int main(){
D* pA = new D();
pA->A::f(); //!
return 0;
}
Is there any way to set p to 42 in the most base class A?
Following construction pA->A::f(); sets p to 42 for non-virtual inherited class A. Can we do that without cast?
First off, there is no cast: you just qualify which version of A you want as there are more than one. Of course, the notation you have chosen actually doesn't work because it doesn't resolve the ambiguity in the first place. I guess you meant to use something like
pA->B::f();
If you don't want to put the burden of choosing which member function to call on the user of your class, you'll have to provide suitable forwarding functions for D e.g.:
void D::f() { this->B::f(); }

Diamond sub-problem: non-multiple inheritance in side branch still require class constructor

Strange problem occurred when I tried to "solve" usual diamond problem in a usual way - using virtual inheritance:
A
/ \* both virtual
B C
\ /
D
However my base class A doesn't have default constructor, so I was to call it manually from D. However when I try to add a class E into this diamond as C-inherited
A
/ \* both virtual
B C
\ / \
D E
it is still needed to call constructor of A in E constructor manually, i.e. C doesn't what to create A from E even though there is neither multiple inheritance nor diamond A-C-E.
class A
{public:
A (int _N): N(_N) {};
void show()
{cout<<"A"<<N;}
protected:
int N;
};
class B: public virtual A
{ public:
B(int n): A(2*n) {};
void show()
{ cout<<"B"<<N;}
};
class C: public virtual A
{ public:
C(int n): A(3*n) {};
void show()
{ cout<<"C"<<N;}
};
class D: public B,C
{ public:
D(): B(1), C(2), A(3) {};
void show()
{ cout<<"D"<<N;}
};
class E: public virtual C
{ public:
E(): C(1) {};
void show()
{ cout<<"E"<<N;}
};
int main()
{D d; // OK
A *a = &d;
a->show();
E e; // NOT OK, no function A::A() to call in E::E()
A *a2 = &e;
a2->show();
return 0;
}
Is it possible to solve this issue without calling constructor of A from E? I need C to do it properly :-).
Or is it possible not to try to solve diamond problem at all:
A A
| | no virtual at all
B C
\ / \
D E
and still try to declare object of class D with two instances of A but telling compiler to use A of C when colling from D each time? When I try to add
using C::A
into the declaration of D it still produce error of unambiguous base A.
Is it possible to solve this issue without calling constructor of A from E? I need C to do it properly :-).
The constructor for the most derived class (in this case, E) is responsible for calling the constructor for any virtual base classes.
The constructor of C cannot call the constructor of A because C is not the most derived class. Virtual base classes are initialized before any direct base classes, so E must initialize by A before it can initialize C.
Yes, virtual base class constructor calls are unlike virtual function overriding:
you can override a virtual function in a derived class;
you must override a virtual function only if the function is overridden in one of the base classes but not in others;
you cannot not override the init-list for virtual base classes of the base class constructor.
This implies that:
as far as virtual function overriding is concerned, virtual inheritance does not affect single inheritance at all;
but when it comes to base class constructor calls, virtual inheritance affects every derived class.