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

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

Related

Can I forbid calling private member of derived class from base?

I has this code:
struct A {
virtual void f() {}
};
struct B: A {
private:
void f() override {}
};
...
B b;
A& a = b;
a.f();
And ofcourse it will call f() from B cause private is checked on the compilation time but choosing virtual function version are in the runtime. Can I forbid f() calling in this case?
No. As you call f() on an A reference, the access rules are checked on A. You can't expect the compiler to check the rules for B, because it does not necessarily know that a is of type B.
From cppreference:
Access rules for the names of virtual functions are checked at the call point using the type of the expression used to denote the object for which the member function is called. The access of the final overrider is ignored:
struct B { virtual int f(); }; // f is public in B
class D : public B { private: int f(); }; // f is private in D
void f() {
D d;
B& b = d;
b.f(); // OK: B::f is public, D::f is invoked even though it's private
d.f(); // error: D::f is private
}
Your code should not rely on this though. In my opinion, access specifiers for a given method should be consistent across the class hierarchy.

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.

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.

multiple virtual inheritance in C++ complains on specific function call

In C++ multiple inheritance with virtual base
i understand why could this code be ambiguous , but why it still complains when i specifically call a derived class method ?
class A {
public:
virtual void f() { cout << 1 ;}
};
class B :virtual public A {
public:
virtual void f() { cout << 2; }
};
class C :virtual public A {
public:
virtual void f() { cout << 3; }
};
class D : public B, public C {};
void main(){
D* d = new D();
d->B::f();
getch();
}
i would expect that it will run the B:f() method but i get :
"ambiguous inheritance of 'void A::f(void)'
The problem is that your function is virtual which means it overrides address in vtable. You can't override one address with two functions.
Without virtual qualifier there would be just ambiguity during the call which can be avoided just like you did specifying base class B::f()
The problem is that your class D has two final overriders of function f. See http://en.cppreference.com/w/cpp/language/virtual about final overrider.

Virtual multiple inheritance

I stumbled upon this code example:
#include <iostream>
using namespace std;
class A {
int x;
public:
A() { x = 1; cout << "A"; }
};
class B : virtual public A {
int y;
public:
B() { y = 2; cout << "B"; }
};
class C : virtual public B, virtual public A {
int z;
public:
C() { z = 3; cout <<"C"; }
};
class D : public A, public B, public C {
int t;
public:
D() { t = 4; cout << "D"; }
};
int main()
{
D d;
return 0;
}
This code prints ABABCD and I have no idea why. I thought it would print A for D : public A, then AB for D : public B, then ABC for D : public C, and then D, but it seems A is only printed twice. How does this work?
The order of construction of the bases is (ignoring virtual bases) left to right as they are typed in the inheritance relationship. Once you add virtual bases, those are initialized first (before any non-virtual base) in a depth-first left-to-right manner.
Now this should explain the output.
D : A, B, C
A has no virtual bases, B has a virtual A base, so that is the first initialized: "A". Then C has a virtual B base, so that is the next one being initialized. At this point the A virtual subobject has already been initialized, so only the B constructor is evaluated "AB". At this point all virtual bases have been constructed and the non-virtual bases are constructed, first A, then B, then C, then the complete type D, yielding "ABABCD". The virtual sub objects have all been constructed, so they don't get constructed again.
Some things to keep in mind. A virtual base is only shared with other subobjects that are willing to share it (i.e. have it as a virtual base). There is no limit as of how many times a virtual base can be shared within a complete object (i.e. the A virtual base is shared multiple times, including from different B subobjects)