I have an idea to architecting classical entity-component in a better way with variadic template inheritance. This question stems from funky experiments in the context of 3d-graphics but i believe i have it broken down to a very abstract question about C++. I am able to use C++20 in the scope in which it is currently implemented in Microsoft cl aka. the MSVC++19-Toolchain.
So. A few Base classes:
class basic {
public:
std::wstring get_name() { return name; }
virtual void do_something_idk_virtual() = 0;
virtual ~basic() {}
private:
std::wstring name;
}
class has_legs {
public:
virtual void walk() = 0;
virtual ~has_legs() {}
}
class has_wings {
public:
virtual void fly() = 0;
virtual ~has_wings() {}
}
template<typename... Ts>
class entity : public basic, public Ts... {
public:
virtual ~entity() {}
}
So far, so good. Now i want to make a duck:
class duck : entity<has_wings, has_legs> {
public:
virtual ~duck() {}
virtual void walk() { cout << "walk" << endl; }
virtual void fly() { cout << "fly" << endl; }
virtual void do_something_idk_virtual() { } // nothing,
}
still, seems to work. The problem is: I know have data structure (say a linked_list, or some sort of graph) and I use the visitor-pattern to work with basic*-typed things. I now have a lot of Code that looks like this. This is, quite literally, the central and critical part of a my program:
void visit(basic* node) {
//here i find out, through magic or some other kind of out-of-scope-mechanism that node is at least a has_wings. Problem:
reinterpret_cast<has_wings*>(node)->fly(); //does not work, will call basic::do_something_idk_virtual(). As far as i understand, this is because the compiler-generated vtable does not change via the reinterpret_cast.
reinterpret_cast<entity<has_wings>*>(node)->fly(); //might, work, problems start to come in if node is of some type that has_wings and has_legs. It sometimes calls some other method, depending on the ordering in declaring the class.
}
Solution
Have every component (aka. the pure interfaces) and the entity-class virtually inherit from basic
in basic add the non-virtual method:
template<typename TComponent> TComponent* get_component() {
return dynamic_cast<TComponent*>(this);
}
This will then fix vtables. I am not sure why dynamic_cast does that.
First of all, your template gives you nothing. class duck : public basic, public has_wings, public has_legs is absolutely identical.
Second, you need to decide what your level of polymorphic access is. If your level is basic, than it has to have already defined all the virtuals you want to be accessing (i.e. has_wings, fly) An interface where you need dynamic_casts to arrive to correct dynamic type (your example with reinterpret_cast is just wrong, you can't use reinterpret_cast to move through class hierarchy) is a poorly written interface.
Sometimes visitor pattern can be employed, but in my mind, it tends to produce extremely hard to troubleshoot code.
You have to use static_cast or dynamic_cast to move within an inheritance hierarchy, and static_cast can’t descend virtual inheritance or cross-cast (in one step) because the layout of different classes that derive from the source and destination types may differ. As it is, you’d have to know the actual type (not just that it had a given aspect) to do the conversion. If you have your “aspect” classes inherit from basic—virtually, so that there is a unique basic* to be had—you can then use dynamic_cast not for its checking purpose but so as to find the appropriate vtable for the aspect in question.
If you can’t afford that, you may want to amalgamate the interfaces somehow. You then have to be careful to call the functions only when they’re meaningful; that’s already the case (“through magic”), but then the ordinary call syntax might be an attractive nuisance. You might also try some C-style polymorphism with a manually-created vtable with function pointers for each optional behavior.
I'm using class to declare interface. I just want to define method signature. This method must be implemented in any non-abstract subclass. I don't need method to be virtual. This is default behaviour in C# BTW (i came from C#/Java world)
However it seems in C++ it is not possible. I either declare method in regular way
void Foo::Method()
and then it is not mandatory to implement it or declare method as "pure virtual"
void virtual Foo::Method() = 0;
and then method become virtual, but I want to avoid this to save performance a little bit.
It seems I want to have something like that
void Foo::Method() = 0;
but that would be compilation error
if you're planning on using the derived class from template code, i.e. compile time polymorphism, then you only need to document the expected signature
the code using a derived class simply won't compile and link if the used function isn't implemented
otherwise, for runtime polymorphism it needs to be virtual, or else it won't be called
I believe that you might be confused with regard to how C# version works:
class A {
public void NonVirt() { Console.Out.WriteLine("A:NonVirt"); }
public virtual void Virt() { Console.Out.WriteLine("A:Virt"); }
}
class B : A {
public void NonVirt() { Console.Out.WriteLine("B:NonVirt"); }
public override void Virt() { Console.Out.WriteLine("B:Virt"); }
}
class Program {
static void Main(string[] args) {
A x = new B();
x.NonVirt();
x.Virt();
}
}
This will output
A:NonVirt
B:Virt
So even in C#, you need to make method virtual if you want to call the derived implementation.
If method must be implemented in all non-abstract subclasses this means that you need to call them through base class pointer. This in turn means that you need to make them virtual, same as in C# (and likely in Java, but I am not sure)
Btw, price of virtual call is a few nanoseconds on modern CPUs, so I am not sure if it is worth it but lets say that it is.
If you want to avoid the cost of virtual call, you should use compile time polymorphism via templates
There is no notion of interface in C++. The only way to achieve your goal is to create a base class defining as virtual and = 0 all the methods which must be actually defined in subclasses.
class IBase {
// ...
virtual void f1() = 0;
// ....
}
That class will be virtual pure if all methods are defined like f1, which is the closest to an interface you can get.
The concept of interface in Java is a bit like a contract with regard to classes implementing it. The compiler enforces the constraints of the contract by checking the content of the implementors. This notion of contract or explicit structural subtyping does not exist formally in C++.
However, you can manually verify that such constraints are respected by defining a template wich will expect as a parameter a class with the defined methods or attributes, and using that template on the classes to be verified. This could be considered a form of unit testing I suppose.
I read an answer some time back to a question regarding dynamic_cast. The dynamic_cast failed to work because the base class had no virtual methods. One of the answers said that deriving from classes with no virtual methods generally means a bad design. Is this correct? Even without taking advantage of polymorphism, I still can't see the wrongness in doing this.
It depends what we're talking about:
for Traits classes (no data) it's fine (std::unary_function comes to mind)
for private inheritance (used instead of composition to benefit from Empty Base Optimization) it's fine too
The problem comes when you starts treating such a Derived object polymorphically wrt this Base class. If you ever attain such a position, then it's definite code smell.
Note: Even when noted as fine above, you are still providing the ability to use the class polymorphically, and you thus expose yourself to subtle bugs.
Deriving from a class is always a valid option, for the sake of code reuse.
Sometimes, we are not looking for polymorphic behavior. That's OK - there's a reason we have that option. If this is the case, though, then consider using private inheritance instead - if your class isn't meant to be polymorphic, then there's no reason for anyone to try to use it polymorphically.
Here is an OK example, to factor behaviors into policies (note the protected destructor):
struct some_policy
{
// Some non-virtual interface here
protected:
~some_policy() { ... }
private:
// Some state here
};
struct some_class : some_policy, some_other_policy { ... };
Another Ok example, to avoid code bloat in templates. Note the protected destructor:
struct base_vector
{
// Put everything which doesn't depend
// on a template parameter here
protected:
~base_vector() { ... }
};
template <typename T>
struct vector : base_vector
{ ... };
Another example, called CRTP. Note the protected destructor:
template <typename Base>
struct some_concept
{
void do_something { static_cast<Base*>(this)->do_some_other_thing(); }
protected:
~some_concept() { ... }
};
struct some_class : some_concept<some_class> { ... };
Another example, called Empty Base Optimization. Not really inheritance per se, since it is more a trick to allow the compiler to reserve no space in some_class for the base class (which acts as a private member).
template <typename T>
struct some_state_which_can_be_empty { ... };
template <typename T>
struct some_class : private some_state_which_can_be_empty<T> { ... };
As a rule of thumb, classes you inherit from should have either virtual or protected destructor.
Inheritance without virtual methods in C++ is nothing more than a code reuse.
I can't think of inheritance without polymorphism.
Some classes in the C++ standard library have protected members (which are only meaningful to a derived class) but no virtual member functions. I.e., they're designed for derivation, without having virtuals. This proves that it must generally be bad design to derive from a class without virtuals.
Cheers & hth.,
I know that it's OK for a pure virtual function to have an implementation. However, why it is like this? Is there conflict for the two concepts? What's the usage? Can any one offer any example?
In Effective C++, Scott Meyers gives the example that it is useful when you are reusing code through inheritance. He starts with this:
struct Airplane {
virtual void fly() {
// fly the plane
}
...
};
struct ModelA : Airplane { ... };
struct ModelB : Airplane { ... };
Now, ModelA and ModelB are flown the same way, and that's believed to be a common way to fly a plane, so the code is in the base class. However, not all planes are flown that way, and we intend planes to be polymorphic, so it's virtual.
Now we add ModelC, which must be flown differently, but we make a mistake:
struct ModelC : Airplane { ... (no fly function) };
Oops. ModelC is going to crash. Meyers would prefer the compiler to warn us of our mistake.
So, he makes fly pure virtual in Airplane with an implementation, and then in ModelA and ModelB, put:
void fly() { Airplane::fly(); }
Now unless we explictly state in our derived class that we want the default flying behaviour, we don't get it. So instead of just the documentation telling us all the things we need to check about our new model of plane, the compiler tells us too.
This does the job, but I think it's a bit weak. Ideally we instead have a BoringlyFlyable mixin containing the default implementation of fly, and reuse code that way, rather than putting code in a base class that assumes certain things about airplanes which are not requirements of airplanes. But that requires CRTP if the fly function actually does anything significant:
#include <iostream>
struct Wings {
void flap() { std::cout << "flapping\n"; }
};
struct Airplane {
Wings wings;
virtual void fly() = 0;
};
template <typename T>
struct BoringlyFlyable {
void fly() {
// planes fly by flapping their wings, right? Same as birds?
// (This code may need tweaking after consulting the domain expert)
static_cast<T*>(this)->wings.flap();
}
};
struct PlaneA : Airplane, BoringlyFlyable<PlaneA> {
void fly() { BoringlyFlyable<PlaneA>::fly(); }
};
int main() {
PlaneA p;
p.fly();
}
When PlaneA declares inheritance from BoringlyFlyable, it is asserting via interface that it is valid to fly it in the default way. Note that BoringlyFlyable could define pure virtual functions of its own: perhaps getWings would be a good abstraction. But since it's a template it doesn't have to.
I've a feeling that this pattern can replace all cases where you would have provided a pure virtual function with an implementation - the implementation can instead go in a mixin, which classes can inherit if they want it. But I can't immediately prove that (for instance if Airplane::fly uses private members then it requires considerable redesign to do it this way), and arguably CRTP is a bit high-powered for the beginner anyway. Also it's slightly more code that doesn't actually add functionality or type safety, it just makes explicit what is already implicit in Meyer's design, that some things can fly just by flapping their wings whereas others need to do other stuff instead. So my version is by no means a total shoo-in.
Was addressed in GotW #31. Summary:
There are three main reasons you might
do this. #1 is commonplace, #2 is
pretty rare, and #3 is a workaround
used occasionally by advanced
programmers working with weaker
compilers.
Most programmers should only ever use #1.
... Which is for pure virtual destructors.
There is no conflict with the two concepts, although they are rarely used together (as OO purists can't reconcile it, but that's beyond the scope of this question/answer).
The idea is that the pure virtual function is given an implementation while at the same time forcing subclasses to override that implementation. The subclasses may invoke the base class function to provide some default behavior. The base cannot be instantiated (it is "abstract") because the virtual function(s) is pure even though it may have an implementation.
Wikipedia sums this up pretty well:
Although pure virtual methods
typically have no implementation in
the class that declares them, pure
virtual methods in C++ are permitted
to contain an implementation in their
declaring class, providing fallback or
default behaviour that a derived class
can delegate to if appropriate.
Typically you don't need to provide base class implementations for pure virtuals. But there is one exception: pure virtual destructors. In fact if your base class has a pure virtual destructor, it must have an implementation. Why would you need a pure virtual destructor instead of just a virtual one? Typically, in order to make a base class abstract without requiring the implementation of any other method. For example, in a class where you might reasonably use the default implementation for any method, but you still don't want people to instantiate the base class, you can mark only the destructor as pure virtual.
EDIT:
Here's some code that illustrates a few ways to call the base implementation:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0;
};
class Der : public Base
{
public:
void DoIt();
};
void Base::DoIt()
{
cout << "Base" << endl;
}
void Der::DoIt()
{
cout << "Der" << endl;
Base::DoIt();
}
int main()
{
Der d;
Base* b = &d;
d.DoIt();
b->DoIt(); // note that Der::DoIt is still called
b->Base::DoIt();
return 0;
}
That way you can provide a working implementation but still require the child class implementer to explicitely call that implementation.
Well, we have some great answers already.. I'm to slow at writing..
My thought would be for instance an init function that has try{} catch{}, meaning it shouldn't be placed in a constructor:
class A {
public:
virtual bool init() = 0 {
... // initiate stuff that couldn't be made in constructor
}
};
class B : public A{
public:
bool init(){
...
A::init();
}
};
My style of coding includes the following idiom:
class Derived : public Base
{
public :
typedef Base super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
This enables me to use "super" as an alias to Base, for example, in constructors:
Derived(int i, int j)
: super(i), J(j)
{
}
Or even when calling the method from the base class inside its overridden version:
void Derived::foo()
{
super::foo() ;
// ... And then, do something else
}
It can even be chained (I have still to find the use for that, though):
class DerivedDerived : public Derived
{
public :
typedef Derived super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
void DerivedDerived::bar()
{
super::bar() ; // will call Derived::bar
super::super::bar ; // will call Base::bar
// ... And then, do something else
}
Anyway, I find the use of "typedef super" very useful, for example, when Base is either verbose and/or templated.
The fact is that super is implemented in Java, as well as in C# (where it is called "base", unless I'm wrong). But C++ lacks this keyword.
So, my questions:
is this use of typedef super common/rare/never seen in the code you work with?
is this use of typedef super Ok (i.e. do you see strong or not so strong reasons to not use it)?
should "super" be a good thing, should it be somewhat standardized in C++, or is this use through a typedef enough already?
Edit: Roddy mentionned the fact the typedef should be private. This would mean any derived class would not be able to use it without redeclaring it. But I guess it would also prevent the super::super chaining (but who's gonna cry for that?).
Edit 2: Now, some months after massively using "super", I wholeheartedly agree with Roddy's viewpoint: "super" should be private.
Bjarne Stroustrup mentions in Design and Evolution of C++ that super as a keyword was considered by the ISO C++ Standards committee the first time C++ was standardized.
Dag Bruck proposed this extension, calling the base class "inherited." The proposal mentioned the multiple inheritance issue, and would have flagged ambiguous uses. Even Stroustrup was convinced.
After discussion, Dag Bruck (yes, the same person making the proposal) wrote that the proposal was implementable, technically sound, and free of major flaws, and handled multiple inheritance. On the other hand, there wasn't enough bang for the buck, and the committee should handle a thornier problem.
Michael Tiemann arrived late, and then showed that a typedef'ed super would work just fine, using the same technique that was asked about in this post.
So, no, this will probably never get standardized.
If you don't have a copy, Design and Evolution is well worth the cover price. Used copies can be had for about $10.
I've always used "inherited" rather than super. (Probably due to a Delphi background), and I always make it private, to avoid the problem when the 'inherited' is erroneously omitted from a class but a subclass tries to use it.
class MyClass : public MyBase
{
private: // Prevents erroneous use by other classes.
typedef MyBase inherited;
...
My standard 'code template' for creating new classes includes the typedef, so I have little opportunity to accidentally omit it.
I don't think the chained "super::super" suggestion is a good idea- If you're doing that, you're probably tied in very hard to a particular hierarchy, and changing it will likely break stuff badly.
One problem with this is that if you forget to (re-)define super for derived classes, then any call to super::something will compile fine but will probably not call the desired function.
For example:
class Base
{
public: virtual void foo() { ... }
};
class Derived: public Base
{
public:
typedef Base super;
virtual void foo()
{
super::foo(); // call superclass implementation
// do other stuff
...
}
};
class DerivedAgain: public Derived
{
public:
virtual void foo()
{
// Call superclass function
super::foo(); // oops, calls Base::foo() rather than Derived::foo()
...
}
};
(As pointed out by Martin York in the comments to this answer, this problem can be eliminated by making the typedef private rather than public or protected.)
FWIW Microsoft has added an extension for __super in their compiler.
I don't recall seeing this before, but at first glance I like it. As Ferruccio notes, it doesn't work well in the face of MI, but MI is more the exception than the rule and there's nothing that says something needs to be usable everywhere to be useful.
Super (or inherited) is Very Good Thing because if you need to stick another inheritance layer in between Base and Derived, you only have to change two things: 1. the "class Base: foo" and 2. the typedef
If I recall correctly, the C++ Standards committee was considering adding a keyword for this... until Michael Tiemann pointed out that this typedef trick works.
As for multiple inheritance, since it's under programmer control you can do whatever you want: maybe super1 and super2, or whatever.
I just found an alternate workaround. I have a big problem with the typedef approach which bit me today:
The typedef requires an exact copy of the class name. If someone changes the class name but doesn't change the typedef then you will run into problems.
So I came up with a better solution using a very simple template.
template <class C>
struct MakeAlias : C
{
typedef C BaseAlias;
};
So now, instead of
class Derived : public Base
{
private:
typedef Base Super;
};
you have
class Derived : public MakeAlias<Base>
{
// Can refer to Base as BaseAlias here
};
In this case, BaseAlias is not private and I've tried to guard against careless usage by selecting an type name that should alert other developers.
I've seen this idiom employed in many code bases and I'm pretty sure I've even seen it somewhere in Boost's libraries. However, as far as I remember the most common name is base (or Base) instead of super.
This idiom is especially useful if working with class templates. As an example, consider the following class (from a real project):
template <typename TText, typename TSpec>
class Finder<Index<TText, PizzaChili<TSpec>>, MyFinderType>
: public Finder<Index<TText, MyFinderImpl<TSpec>>, Default>
{
using TBase = Finder<Index<TText, MyFinderImpl<TSpec>>, Default>;
// …
}
The inheritance chain uses type arguments to achieve compile-time polymorphism. Unfortunately, the nesting level of these templates gets quite high. Therefore, meaningful abbreviations for the full type names are crucial for readability and maintainability.
I've quite often seen it used, sometimes as super_t, when the base is a complex template type (boost::iterator_adaptor does this, for example)
is this use of typedef super common/rare/never seen in the code you work with?
I have never seen this particular pattern in the C++ code I work with, but that doesn't mean it's not out there.
is this use of typedef super Ok (i.e. do you see strong or not so strong reasons to not use it)?
It doesn't allow for multiple inheritance (cleanly, anyway).
should "super" be a good thing, should it be somewhat standardized in C++, or is this use through a typedef enough already?
For the above cited reason (multiple inheritance), no. The reason why you see "super" in the other languages you listed is that they only support single inheritance, so there is no confusion as to what "super" is referring to. Granted, in those languages it IS useful but it doesn't really have a place in the C++ data model.
Oh, and FYI: C++/CLI supports this concept in the form of the "__super" keyword. Please note, though, that C++/CLI doesn't support multiple inheritance either.
One additional reason to use a typedef for the superclass is when you are using complex templates in the object's inheritance.
For instance:
template <typename T, size_t C, typename U>
class A
{ ... };
template <typename T>
class B : public A<T,99,T>
{ ... };
In class B it would be ideal to have a typedef for A otherwise you would be stuck repeating it everywhere you wanted to reference A's members.
In these cases it can work with multiple inheritance too, but you wouldn't have a typedef named 'super', it would be called 'base_A_t' or something like that.
--jeffk++
After migrating from Turbo Pascal to C++ back in the day, I used to do this in order to have an equivalent for the Turbo Pascal "inherited" keyword, which works the same way. However, after programming in C++ for a few years I stopped doing it. I found I just didn't need it very much.
I was trying to solve this exact same problem; I threw around a few ideas, such as using variadic templates and pack expansion to allow for an arbitrary number of parents, but I realized that would result in an implementation like 'super0' and 'super1'. I trashed it because that would be barely more useful than not having it to begin with.
My Solution involves a helper class PrimaryParent and is implemented as so:
template<typename BaseClass>
class PrimaryParent : virtual public BaseClass
{
protected:
using super = BaseClass;
public:
template<typename ...ArgTypes>
PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){}
}
Then which ever class you want to use would be declared as such:
class MyObject : public PrimaryParent<SomeBaseClass>
{
public:
MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {}
}
To avoid the need to use virtual inheritance in PrimaryParenton BaseClass, a constructor taking a variable number of arguments is used to allow construction of BaseClass.
The reason behind the public inheritance of BaseClass into PrimaryParent is to let MyObject have full control over over the inheritance of BaseClass despite having a helper class between them.
This does mean that every class you want to have super must use the PrimaryParent helper class, and each child may only inherit from one class using PrimaryParent (hence the name).
Another restriction for this method, is MyObject can inherit only one class which inherits from PrimaryParent, and that one must be inherited using PrimaryParent. Here is what I mean:
class SomeOtherBase : public PrimaryParent<Ancestor>{}
class MixinClass {}
//Good
class BaseClass : public PrimaryParent<SomeOtherBase>, public MixinClass
{}
//Not Good (now 'super' is ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public SomeOtherBase{}
//Also Not Good ('super' is again ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public PrimaryParent<SomeOtherBase>{}
Before you discard this as an option because of the seeming number of restrictions and the fact there is a middle-man class between every inheritance, these things are not bad.
Multiple inheritance is a strong tool, but in most circumstances, there will be only one primary parent, and if there are other parents, they likely will be Mixin classes, or classes which don't inherit from PrimaryParent anyways. If multiple inheritance is still necessary (though many situations would benefit to use composition to define an object instead of inheritance), than just explicitly define super in that class and don't inherit from PrimaryParent.
The idea of having to define super in every class is not very appealing to me, using PrimaryParent allows for super, clearly an inheritence based alias, to stay in the class definition line instead of the class body where the data should go.
That might just be me though.
Of course every situation is different, but consider these things i have said when deciding which option to use.
I don't know whether it's rare or not, but I've certainly done the same thing.
As has been pointed out, the difficulty with making this part of the language itself is when a class makes use of multiple inheritance.
I use this from time to time. Just when I find myself typing out the base class type a couple of times, I'll replace it with a typedef similar to yours.
I think it can be a good use. As you say, if your base class is a template it can save typing. Also, template classes may take arguments that act as policies for how the template should work. You're free to change the base type without having to fix up all your references to it as long as the interface of the base remains compatible.
I think the use through the typedef is enough already. I can't see how it would be built into the language anyway because multiple inheritence means there can be many base classes, so you can typedef it as you see fit for the class you logically feel is the most important base class.
I use the __super keyword. But it's Microsoft specific:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
I won't say much except present code with comments that demonstrates that super doesn't mean calling base!
super != base.
In short, what is "super" supposed to mean anyway? and then what is "base" supposed to mean?
super means, calling the last implementor of a method (not base method)
base means, choosing which class is default base in multiple inheritance.
This 2 rules apply to in class typedefs.
Consider library implementor and library user, who is super and who is base?
for more info here is working code for copy paste into your IDE:
#include <iostream>
// Library defiens 4 classes in typical library class hierarchy
class Abstract
{
public:
virtual void f() = 0;
};
class LibraryBase1 :
virtual public Abstract
{
public:
void f() override
{
std::cout << "Base1" << std::endl;
}
};
class LibraryBase2 :
virtual public Abstract
{
public:
void f() override
{
std::cout << "Base2" << std::endl;
}
};
class LibraryDerivate :
public LibraryBase1,
public LibraryBase2
{
// base is meaningfull only for this class,
// this class decides who is my base in multiple inheritance
private:
using base = LibraryBase1;
protected:
// this is super! base is not super but base!
using super = LibraryDerivate;
public:
void f() override
{
std::cout << "I'm super not my Base" << std::endl;
std::cout << "Calling my *default* base: " << std::endl;
base::f();
}
};
// Library user
struct UserBase :
public LibraryDerivate
{
protected:
// NOTE: If user overrides f() he must update who is super, in one class before base!
using super = UserBase; // this typedef is needed only so that most derived version
// is called, which calls next super in hierarchy.
// it's not needed here, just saying how to chain "super" calls if needed
// NOTE: User can't call base, base is a concept private to each class, super is not.
private:
using base = LibraryDerivate; // example of typedefing base.
};
struct UserDerived :
public UserBase
{
// NOTE: to typedef who is super here we would need to specify full name
// when calling super method, but in this sample is it's not needed.
// Good super is called, example of good super is last implementor of f()
// example of bad super is calling base (but which base??)
void f() override
{
super::f();
}
};
int main()
{
UserDerived derived;
// derived calls super implementation because that's what
// "super" is supposed to mean! super != base
derived.f();
// Yes it work with polymorphism!
Abstract* pUser = new LibraryDerivate;
pUser->f();
Abstract* pUserBase = new UserBase;
pUserBase->f();
}
Another important point here is this:
polymorphic call: calls downward
super call: calls upwards
inside main() we use polymorphic call downards that super calls upwards, not really useful in real life, but it demonstrates the difference.
The simple answer why c++ doesn't support "super" keyword is.
DDD(Deadly Diamond of Death) problem.
in multiple inheritance. Compiler will confuse which is superclass.
So which superclass is "D"'s superclass?? "Both" cannot be solution because "super" keyword is pointer.
This is a method I use which uses macros instead of a typedef. I know that this is not the C++ way of doing things but it can be convenient when chaining iterators together through inheritance when only the base class furthest down the hierarchy is acting upon an inherited offset.
For example:
// some header.h
#define CLASS some_iterator
#define SUPER_CLASS some_const_iterator
#define SUPER static_cast<SUPER_CLASS&>(*this)
template<typename T>
class CLASS : SUPER_CLASS {
typedef CLASS<T> class_type;
class_type& operator++();
};
template<typename T>
typename CLASS<T>::class_type CLASS<T>::operator++(
int)
{
class_type copy = *this;
// Macro
++SUPER;
// vs
// Typedef
// super::operator++();
return copy;
}
#undef CLASS
#undef SUPER_CLASS
#undef SUPER
The generic setup I use makes it very easy to read and copy/paste between the inheritance tree which have duplicate code but must be overridden because the return type has to match the current class.
One could use a lower-case super to replicate the behavior seen in Java but my coding style is to use all upper-case letters for macros.