Why are pure virtual methods not overridden from a different inheritance branch? - c++

I have this, maybe a little bit complex class hierarchy:
class BS {
public:
virtual void meth()=0;
};
class BCA : public virtual BS {
};
class BSS : public virtual BS {
};
class BCS : public virtual BCA, public virtual BSS {
};
class BI4 {
public:
void meth() {};
};
class BT4 : public virtual BI4, public virtual BSS {
};
class T4 : public virtual BCS, public virtual BT4 {
};
int main() {
T4 t4;
};
Now the problem is that although the void meth() is available in the inheritance graph, despite this code won't compile:
$ g++ -c t.cc -std=c++11
t.cc: In function ‘int main()’:
t.cc:27:6: error: cannot declare variable ‘t4’ to be of abstract type ‘T4’
T4 t4;
^
t.cc:23:7: note: because the following virtual functions are pure within ‘T4’:
class T4 : public virtual BCS, public virtual BT4 {
^
t.cc:3:18: note: virtual void BS::meth()
virtual void meth()=0;
^
t.cc:3:18: note: virtual void BS::meth()
It seems to me as if BS somehow wouldn't see the overloaded meth() method through the BS->BCA->BCS->T4->BT4->BI4 chain.
But why? The method is clearly available, the C3 linearization algorithm used by the C++ should be able very clearly find it.

The rules of the language don't allow it. A virtual function can only be overridden by the declaration of a function with the same name and parameters in a derived class. Since BI4 isn't derived from BS, BI4::meth cannot override BS::meth. If a class inherits (directly or indirectly) from both BS and BI4, then it inherits two functions called meth: one from BS, still abstract and not overridden, and one from BI4.

There are two main aspects:
A given class can only override member functions from its base classes.
Since your BI4 class doesn't have BS as a base class, it can't override anything from BS.
It's possible to inherit in an implementation of a pure virtual function defined in a virtual base class, much like in Java, but the class providing that implementation must itself also have that virtual base class.
Example:
struct Base
{
virtual void foo() = 0;
};
#ifdef GOOD
struct Impl_foo: virtual Base
{
void foo() override {}
};
#else
struct Impl_foo
{
virtual void foo() {}
};
#endif
struct Abstract_derived: virtual Base
{};
struct Derived
: Abstract_derived
, Impl_foo // Java-like implementation inheritance.
// In C++ called "by dominance".
{};
auto main()
-> int
{
Derived o;
o.foo();
}
Without defining the GOOD macro symbol, this code doesn't compile.

BI4 does not inherit from BS either directly or indirectly, so its method BI4::meth() is entirely unrelated and cannot override BS::meth().
You can only override methods from base classes, not from "sibling" or "uncle" classes.

Related

Why does an interface needs to be redeclared?

I have an abstract base class and want to implement a function in the derived class. Why do I have to declare the function in the derived class again?
class base {
public:
virtual int foo(int) const = 0;
};
class derived : public base {
public:
int foo(int) const; // Why is this required?
};
int derived::foo(int val) const { return 2*val; }
Consider that the derived-class definition might be in a header, whereas its implementation may be in a source file. The header typically gets included in multiple locations ("translation units"), each of which will be compiled independently. If you didn't declare the override, then the compiler wouldn't know about it in any of those other translation units.
The intention of making a function pure virtual in Base class is that the derived class must override it and provide its own implementation.
Note that presence of an pure virtual function in the class makes that class an Abstract class. In simple terms the class acts as an interface for creating more concrete classes.One cannot create objects of an Abstract class.
If you do not override the pure virtual function in derived class then the derived class contains the inherited Base class pure virtual function only and it itself acts as an Abstract class too.Once your derived class is abstract it cannot be instantiated.
So in order that your derived class be instantiated it needs to override and hence declare the pure virtual function.
You might think the compiler can deduce that you're going to have to provide an implementation of derived::foo(), but derived could also be an abstract class (and in fact that's what you'll get if you dont declare foo() in derived)
It is to override the abstraction of the base class.
If you do not re-declare it, then your derived class is also an abstract class. If you do then you now have a non-abstract type of the base.
Because the hierarchy could have more layers.
struct Base {
virtual void foo() const = 0;
virtual void bar() const = 0;
};
struct SuperBase: Base {
virtual void bar() const override;
};
struct Concrete: SuperBase {
virtual void foo() const override;
};
Here, SuperBase does not provide an implementation for foo, this needs be indicated somehow.
While you can't instantiate a class with pure virtual functions, you can still create a class like this:
class base {
public:
virtual int foo(int) const = 0;
};
class derived : public base {
public:
};
class very_derived : public derived {
public:
virtual int foo(int) const { return 2; }
};
The derived class is still an abstract class, it can't be instantiated since it doesn't override foo. You need to declare a non-pure virtual version of foo before you can instantiate the class, even if you don't define foo right away.

c++ virtual functions in inheriting class

I know that when you want to declare a polymorphic function you have to declare the base class function virtual.
class Base
{
public:
virtual void f();
};
My question is are you required to declare the inheriting class function as virtual, even if it's expected that Child behaves as if it were "sealed"?
class Child : public Base
{
public:
void f();
};
No, you don't need to re-declare the function virtual.
A virtual function in a base class will automatically declare all overriding functions as virtual:
struct A
{
void foo(); //not virtual
};
struct B : A
{
virtual void foo(); //virtual
}
struct C : B
{
void foo(); //virtual
}
Declaring f() as virtual in Child helps some one reading the definition of Child. It is useful as documentation.
Once the base class override is marked as virtual all other overrides are implicitly so. While you are not required to mark the function as virtual I tend to do so for documentation purposes.
As of the last part: even if it's expected that Child behaves as if it were "sealed"?, if you want to seal the class, you can actually do it in C++11 (this was not fully implementable in C++03 generically) by creating a seal class like so:
template <typename T>
class seal {
seal() {}
friend T;
};
And then inheriting your sealed class from it (CRTP):
class Child : public Base, virtual seal<Child> {
// ...
};
The trick is that because of the use of virtual inheritance, the most derived type in the hierarchy must call the virtual base constructor (int this case seal<Child>), but that constructor is private in the templated class, and only available to Child through the friend declaration.
In C++ you would have to either create a seal type for every class that you wanted to seal, or else use a generic approach that did not provide a perfect seal (it could be tampered with)

Why can't overridden virtual method see overloaded base classes version without explicit scoping?

I don't understand why the compiler doesn't like this, here is and example of the problem:
class A
{
public:
virtual void Expand() { }
virtual void Expand(bool flag) { }
};
class B : public A
{
public:
virtual void Expand() {
A::Expand(true);
Expand(true);
}
};
When I try to compile this the A::Expand(true); compiles fine, but the non scoped Expand(true); gets this compiler error:
'B::Expand' : function does not take 1 arguments
You don't need virtual methods for that behaviour. Methods in a derived class hide methods with the same name in the base class. So if you have any function named Expand in your derived class (even if it is an override of a virtual method from the base class), none of the base classes methods with the same name are visible, regardless of their signature.
You can make the base classes methods visible with using. For that you would add using A::Expand; to the definition of B:
class B : public A
{
public:
using A::Expand;
virtual void Expand() { Expand(true); }
};
That's because besides overriding the base Expand(), you're also hiding the base Expand(bool).
When you introduce a member function in a derived class with the same name as a method from the base class, all base class methods with that name are hidden in the derived class.
You can fix this by either qualifying (as you have) or with the using directive:
class B : public A
{
public:
using A::Expand;
virtual void Expand() {
A::Expand(true);
Expand(true);
}
};

Multiple inheritance and pure virtual functions

The following code:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : public interface_base
{
void foo();
};
struct implementation : public implementation_base, public interface
{
void bar();
};
int main()
{
implementation x;
}
fails to compile with the following errors:
test.cpp: In function 'int main()':
test.cpp:23:20: error: cannot declare variable 'x' to be of abstract type 'implementation'
test.cpp:16:8: note: because the following virtual functions are pure within 'implementation':
test.cpp:3:18: note: virtual void interface_base::foo()
I have played around with it and figured out that making the 'interface -> interface_base' and 'implementation_base -> interface_base' inheritances virtual, fixes the problem, but I don't understand why. Can someone please explain what is going on?
p.s. I omitted the virtual destructors on purpose to make the code shorter. Please don't tell me to put them in, I already know :)
You have two interface_base base classes in your inheritance tree. This means you must provide two implementations of foo(). And calling either of them will be really awkward, requiring multiple casts to disambiguate. This usually is not what you want.
To resolve this, use virtual inheritance:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : virtual public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : virtual public interface_base
{
void foo();
};
struct implementation : public implementation_base, virtual public interface
{
void bar();
};
int main()
{
implementation x;
}
With virtual inheritance, only one instance of the base class in question is created in the inheritance heirarchy for all virtual mentions. Thus, there's only one foo(), which can be satisfied by implementation_base::foo().
For more information, see this prior question - the answers provide some nice diagrams to make this all more clear.
The usual C++ idiom is:
public virtual inheritance for interface classes
private non-virtual inheritance for implementation classes
In this case we would have:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : virtual public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : virtual public interface_base
{
void foo();
};
struct implementation : private implementation_base,
virtual public interface
{
void bar();
};
In implementation, the unique interface_base virtual base is :
publicly inherited via interface: implementation --public--> interface --public--> interface_base
privately inherited via implementation_base: implementation --private--> implementation_base --public--> interface_base
When client code does one of these derived to base conversions:
derived to base pointer conversions,
reference binding of base type with an initializer of static type derived,
access to inherited base class members via a lvalue of derived static type,
what matters is only that there is a least one accessible inheritance path from the derived class to the given base class subobject; other inaccessible paths are simply ignored. Because inheritance of the base class is only virtual here, there is only one base class subject so these conversions are never ambiguous.
Here, the conversion from implementation to interface_base, can always be done by client code via interface; the other inaccessible path does not matter at all. The unique interface_base virtual base is publicly inherited from implementation.
In many cases, the implementation classes (implementation, implementation_base) will be kept hidden from client code: only pointers or references to the interface classes (interface, interface_base) will be exposed.
For the case of 'solving' the diamond inheritance problem, the solutions offered by bdonlan are valid. Having said that, you can avoid the diamond-problem with design. Why must every instance of a given class be seen as both classes? Are you ever going to pass this same object to a class that says something like:
void ConsumeFood(Food *food);
void ConsumeDrink(Drink *drink);
class NutritionalConsumable {
float calories() = 0;
float GetNutritionalValue(NUTRITION_ID nutrition) = 0;
};
class Drink : public NutritionalConsumable {
void Sip() = 0;
};
class Food : public NutritionalConsumable {
void Chew() = 0;
};
class Icecream : public Drink, virtual public Food {};
void ConsumeNutrition(NutritionalConsumable *consumable) {
ConsumeFood(dynamic_cast<Food*>(food));
ConsumeDrink(dynamic_cast<Drink*>(drink));
}
// Or moreso
void ConsumeIcecream(Icecream *icecream) {
ConsumeDrink(icecream);
ConsumeFood(icecream);
}
Surely it would be better in this case for Icecream to just implement NutritionalConsumable and provide a GetAsDrink() and GetAsFood() method that will return a proxy, purely for the sake of appearing as either food or drink. Otherwise that suggests that there is a method or object that accepts a Food but somehow wants to later see it as a Drink, which can only be achieved with a dynamic_cast, and needn't be the case with a more appropriate design.

Factorizing implementations of pure, virtual functions for a subset of classes

Suppose I have this class hierarchy:
class A {
public:
virtual void foo(Base *b) = 0;
};
class B {
public:
virtual void bar() = 0;
};
class C : public A, public B {
public:
virtual void viz() = 0;
};
class E : public C {
public:
/** Some non-pure virtual functions, does not implement foo */
};
class F : public E {
/** does not implement foo */
}
class G : public E {
/** does not implement foo */
}
Now, I would like to define derived, "templatized" versions of F and G, which declares a method foo(T *s) and where the implementation of foo(Base *s) simply calls foo(T *s) by applying a static_cast.
Moreover, I don't wan to expose the template arguments to A, because users of these interfaces (A, B, C, E) should not be exposed to the template parameters (I'm writing a plugin architecture and user-types that derives a given interface are passed back and forth from plugins to base system).
What I thought was to define this class:
template <typename T>
class GA : public A{
...
}
and to define the "templatized" derivations of F and G like this:
template <typename T>
class GF : public F, public GA {
...
}
the problem is that GF sees two different declarations of foo() (one given by A, the other by GA), so the compiler throws me an error when I try to instantiate GF because foo(Base) is not defined.
How can I solve this?
Why do you need multiple inheritance? Why not just class C : public Impl { ... }?
I think the problem is that Impl inherits class A and class C inherits both Impl and A. This means there will be two instances of class A where first A's foo is implemented by Impl and a seconds one is still pure virtual. Virtual inheritance can solve this problem, for example:
class A {
public:
virtual void foo () = 0;
};
class Impl : virtual public A {
public:
virtual void foo () {}
};
class C
: virtual public A
, public Impl
{
};
I would suggest something different. Declare the function as a pure virtual function, and then just trivially implement it in the derived classes by forwarding the call:
class A {
public:
virtual void foo() = 0;
};
void A::foo() {}
class B : public A {
void foo() { A::foo(); }
};
But that does not mean that you cannot achieve what you are trying to do. If you want to implement that operation in another class and use inheritance, you can do it in two different ways:
Intermediate type:
Implement an intermediate type that provides the functionality, then each derived type can extend either the original A or the intermediate I type:
class I : public A {
public:
void foo() {}
};
class B : public I {}; // inherits the implementation
class C : public A { // needs to provide it's own implementation
void foo() {}
};
Virtual inheritance:
Alternatively you can use virtual inheritance (of the three options I would avoid this). In this way you can create a hierarchy where V inherits virtually from A and provides the implementation. The rest of the classes can either inherit from A directly or inherit from A virtually and also inherit from V. By using virtual inheritance you ensure that there is a single A subobject in your hierarchy:
class V : public virtual A {
public:
void foo() {}
};
class B : public virtual A, V { // Inheritance from V is a detail, can be private
// no need to provide foo
};
Note that this approach is overkill for this particular limited problem, and that it has side effects (the layout of the objects will be different and they will be slightly bigger in size). Again, unless you want to mix and match functions from different sibling classes, I would avoid this.
What was wrong in your code
The problem in your code is that you are inheriting from A multiple times, one through D and another directly. While in the path from D the virtual function is no longer pure, in the direct path from A the virtual function is still undefined.
That also causes the second issue in the error message: ambiguity. Because there are two A subobjects, when you try to call foo on the most derived type, the compiler cannot figure out on which of the two A subobjects you want to execute the request. The same type of ambiguity problems would arise if you tried to cast from the most derived type into A directly, as the compiler would not know to which A you want to refer.
Impl does not implement A's method in D and C, it's a completely different base.
You should derive Impl from A, then derive every class of that subset from Impl instead of A.
If you want to inherit from both the A and Impl, then you need to use virtual inheritance.
Edit:
See #Vlad's answer for more details.