virtual inheritance and signature overlapping - c++

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(); }

Related

Why is dynamic_cast to a non-unique base class type allowed?

From https://en.cppreference.com/w/cpp/language/dynamic_cast:
dynamic_cast < new_type > ( expression )
3) If new_type is a pointer or reference to Base, and the type of expression is a pointer or reference to Derived, where Base is a unique, accessible base class of Derived, the result is a pointer or reference to the Base class subobject within the Derived object pointed or identified by expression. (Note: an implicit conversion and static_cast can perform this conversion as well.)
Sample code:
#include <iostream>
using namespace std;
class A {
//public:
// virtual ~A() {
//
// }
};
class B : public A {
};
class C : public B {
};
class D : public B, public A {
};
int main()
{
D* pd = new D;
if (B* pa = dynamic_cast<B*>(pd)) {
cout << "1";
}
return 0;
}
No error or warning under VC++
warning: direct base 'A' inaccessible in 'D' due to ambiguity under gcc, link
Shouldn't I expect a compile error?
Now I find that if I try to convert D* to A*, a error would occur, but as mentioned above, from D* to B*, no error.
int main()
{
D* pd = new D;
if (A* pa = dynamic_cast<A*>(pd)) {
cout << "1";
}
return 0;
}
link
In that context, unique means that Derived contains new_type only once, not that Derived derives from a single base class.
So, in your example, B is unique, because D contains it only once.
In your example, D contains A twice (once directly, and once through B), so a cast to A cannot be made, as A is not unique.
Note, that "containment" what it counts. So, in this example, C derives from Base twice, yet it is fine, as Base is inherited with the keyword virtual:
struct Base { };
struct A: virtual Base { };
struct B: virtual Base { };
struct C: A, B { };
int main() {
C c;
dynamic_cast<Base &>(c);
}
(If I haven't used virtual, then Base would have been ambiguous)
Note: I'd use static_cast instead, as it can do the cast in this case as well. Using dynamic_cast is a little bit misleading here, as the cast will be done compile-time, and not run-time.
The warning causes by indirect base class issue in multiple-inheritance model. D has two copies of base class A, one directly and one indirectly via B. When a base class is specified as a virtual base (same as #geza example), there is only one copy of base class' data members shared between virtual base classes.
class A {
public:
A() {}
};
class B : virtual public A {
public:
B() : A() {}
};
class C : public B, virtual public A {
public:
C() : B() {}
};
int main()
{
A* pa = static_cast<A *>(new C()); // no more warning since A is virtual
return 0;
}
It's well explained here: Multiple Base Classes.

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().

Why does GCC give me an error: no unique final overrider?

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();
}

Interview question about virtual functions in C++

I was asked this crazy question.
I was out of my wits.
Can a method in base class which is declared as virtual be called using the base class pointer which is pointing to a derived class object?
Is this possible?
If you're trying to invoke a virtual method from the base class pointer, yes.
That's polymorphism.
If you're asking, with a base class pointer to a derived class, can you invoke a base class method that is overriden by the derived class? Yes that's also possible by explicitly scoping the base class name:
basePtr->BaseClass::myMethod();
Try:
class A { virtual void foo(); }
class B : public A { virtual void foo(); }
A *b = new B();
b->A::foo ();
You mean something like this. (Where pBase is of type pointer-to-base but the pointed-to object is actually of type Derived which is derived from Base.)
pBase->Base::method();
Yes, it's possible.
Yes -- you have to specify the full name though:
#include <iostream>
struct base {
virtual void print() { std::cout << "Base"; }
};
struct derived : base {
virtual void print() { std::cout << "Derived"; }
};
int main() {
base *b = new derived;
b->base::print();
delete b;
return 0;
}
If I understand the question correctly, you have
class B
{
public:
virtual void foo();
};
class D: public B
{
public:
virtual void foo();
}
B* b = new D;
And the question is, can you call B::foo(). The answer is yes, using
b->B::foo()
class B
{
public:
virtual void foo();
};
class D: public B
{
public:
virtual void foo();
}
B* b = new D;
Try calling
(*b).foo()
to invoke base class foo function
class B {
public: virtual void foo();
};
class D: public B {
public: virtual void foo()
{
B::foo();
};
}
B* b = new D;
Solutions :
b->foo();
b->B::foo()
OR do not override/define foo() in the derived class D and call b->foo()
B objb = *b; objb.foo() ; // this is object slicing and not (*b).foo() as in one of the previous answers
No. Not in a clean way. But yes. You have to do some pointer manipulation, obtain a pointer to the vtable and make a call. but that is not exactly a pointer to base class, but some smart pointer manipulation. Another approach is using scope resolution operator on base class.

Why is there ambiguity in this diamond pattern?

#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