Related
This question is the equivalent of Java's How to make Superclass Method returns instance of SubClass
Suppose this class hierarchy:
class A
{
public:
A makeCopyOfObject();
};
class B: public A
{
public:
void doSomethingB();
};
class C: public A
{
public:
void doSomethingC();
};
I'd like to use it this way:
B().makeCopyOfObject().doSomethingB();
C().makeCopyOfObject().doSomethingC();
But of course I can't, because the makeCopyOfObject function returns an instance of A, not an instance of a subclass.
I surely could write two versions of the function in the two subclasses, but the code would be the same, except for the return type, because all fields to be copied and modified are in the base class. So, is there an alternative?
I don't understand what you try to achieve with that and it is probably not an answer (see comments), but what you can do is a template (in detail a Curiously recurring template pattern).
template<typename CRTP>
class A {
public:
CRTP& makeCopyOfObject() {
return static_cast<CRTP&>(*this);
}
};
class B: public A<B> {
public:
void doSomethingB();
};
class C: public A<C> {
public:
void doSomethingC();
};
And use it as you wanted:
b.makeCopyOfObject().doSomethingB();
c.makeCopyOfObject().doSomethingC();
See running example here.
Sidenote:
Because of laziness, even the function is called makeCopyOfObject it does not create a copy, but returns a reference to the object. To do a copy you have to implement copy constructors and return a copy (search for clone pattern).
It's difficult to see any practical use case for this, but it's easy to do with a non-member function template:
template< class Type >
auto copy_of( Type const& o )
-> Type
{ return o; }
Then you can write
copy_of( B() ).doSomething();
to your hearts' content, instead of just writing
B().doSomething();
Enjoy.
For the academic issue of covariant methods, there is a lot to be said. You might check out answers to how to implement a clone method. Essentially, this is not supported by C++ except for type system support for reference or pointer result, so one must do it "manually", and the three common approaches are (1) fully manual implementation in each class, (2) using a macro that expands to necessary code in each class, and (3) a middle-man class that forwards arguments to base class constructors. With C++03 the third way was about the same order of difficulty as leveraging dominance in a virtual inheritance hierarchy, so with C++03 that was also an option to be mentioned. But it's just not in the right ballpark of practicality with C++11 and later, (1), (2) and (3) it is.
First, beware of slicing.
Second, if you want to make a copy of an object, may I suggest
the copy-constructor:
B b1{};
B b2{b1}.doSomethingB();
or even:
B{ B{} }.doSomethingB();
Also, if you want to preserve polymorphic behaviour, always
handle polymorphic classes via smart-pointers or references.
Note that C++ has value-semantics, while Java has reference-semantics, so design-patterns that are good for Java are not necessarily good for C++.
Being new to C++, I have trouble understanding a linkage problem I'm having. The following is the file that's giving me a headache:
#pragma once
#include <string>
#include "finite_automaton.h"
template<typename RL>
class RegularLanguage {
public:
bool empty();
RegularLanguage<RL> minimize();
RegularLanguage<RL> complement();
RegularLanguage<RL> intersectionWith(RegularLanguage<RL> other);
RegularLanguage<RL> unionWith(RegularLanguage<RL> other);
RegularLanguage<RL> concatenate(RegularLanguage<RL> other);
RegularLanguage<RL> kleeneStar();
/*
* Returns a regular expression that describes the language of this automaton.
*/
std::string string();
bool disjoint(RegularLanguage<RL> other) {
return intersectionWith(other).empty();
}
bool containedIn(RegularLanguage<RL> super) {
return intersectionWith(super.complement()).empty();
}
bool contains(RegularLanguage<RL> sub) {
return complement().intersectionWith(sub).empty();
}
bool equals(RegularLanguage<RL> other) {
return contains(other) && containedIn(other);
}
};
When I compile the project, I get the following errors during the linking phase:
undefined reference to `RegularLanguage<FiniteAutomaton>::complement()'
undefined reference to `RegularLanguage<FiniteAutomaton>::intersectionWith(RegularLanguage<FiniteAutomaton>)'
undefined reference to `RegularLanguage<FiniteAutomaton>::empty()'
both for RegularLanguage<RL>::containedIn(..) and RegularLanguage<RL>::contains(..).
What am I doing wrong? I do get some related errors pertaining to the classes that implement this template class, but I left them out so as not to post unnecessarily much code.
So to summarize what you're trying to do, you have a class template:
template<typename RL> class RegularLanguage {...};
with some methods implemented and others only declared, but not implemented. Then you try to derive from this and in the derived class implement those other methods that weren't implemented in RegularLanguage:
class FiniteAutomaton : public RegularLanguage<FiniteAutomaton>
You seem to be conflating two concepts in C++: inheritance (specifically, polymorphism) and templates.
Your assumption was that by inheriting from RegularLanguage you can implement the missing stuff in the derived class. That's not how templates work, but polymorphism does work that way. You need to either:
Fully implement all methods in the class template RegularLanguage, and then derive FiniteAutomaton from that.
Make RegularLanguage an abstract base class -- not a class template -- with (possibly pure) virtual methods, and derive from that. The methods you want to be implemented in FiniteAutomaton can be. Those may or may not be pure. The methods you don't want to implement in the base class should be declared as pure (=0), and then implemented in the derived class.
It sounds to me like what you're really trying to do would be better accomplished with #2, so here's an incomplete example of how to do that:
class RegularLanguage {
public:
virtual RegularLanguage* Clone() = 0; // Creates a copy of this object
virtual RegularLanguage& complement() = 0; // pure virtual must be implemented in derived class
virtual bool containedIn(RegularLanguage& super) // non-pure, does not have to be implemented in derived
{
return intersectionWith(super.complement()).empty();
}
virtual ~RegularLanguage() {}
};
class FiniteAutomaton
:
public RegularLanguage
{
public:
RegularLanguage* Clone()
{
RegularLanguage* clone = new FiniteAutomaton (*this);
return clone;
}
RegularLanguage* complement()
{
RegularLanguage* comp = this->Clone();
comp->SetSomeStuff();
return comp;
}
};
There are a number of details hidden up there that I haven't mentioned. For one, the return type of complement is now a RegularLanguage pointer, rather than a RegularLanguage by-value. This is required1 in order to avoid the so-called Slicing Problem which would break polymorphism.
For another, I have abandoned the use of templates here because as I've implemented this, there is no apparent need for them. However the use of templates and polymorphism are not completely mutually exclusive. The base class could employ templates, and in fact the base class could be a class template and still be an abstract base class. But the derived class must derive from a concrete instantiation of that base class template. Things get somewhat complicated here. Just be careful that you're not seeing everything as a nail and get carried away with that hammer.
For another, I didn't implement a virtual destructor in the base class before (this is fixed now) but you must2. Just remember this: if a class is intended to be derived from, it should have a virtual destructor in virtually every case.
For another, I've added a Clone method to the base class as a pure virtual. This was in response to the suggestion that complement() shouldn't return this instance of the object, but a new instance which is the complement of this instance. In a polymorphic hierarchy, when we make copies of object we almost always need to do so through the base class pointer, so a Clone() type method is usually present in such a design.
1 "This is required [passing by pointer]: Actually, you could return a reference as well. Return a reference if you can, but here we need to return a pointer. (Actually we should be returning smart pointers, but that's a tangent.)
2 "You must [implement a virtual destructor]: Technically, you need to have a virtual destructor if you intend to delete the object through a base class pointer. I have never in my professional career seen an instance where I could not or should not implement a virtual destructor in the base class of a polymorphic hierarchy. It's close to correct to say you should always do this, even if you have no plans to delete through a base class pointer.
You need to declare bool empty() (and others not implemented in the base) as a pure virtual in the base class:
virtual bool empty() = 0;
and then override/implement them in a descendant class like this:
virtual bool empty(){
...
}
There is a series of problems with this code. The most obvious one has been pointed out: you need your functions pure virtual. But that's just the beginning.
When you make your functions pure virtual, you will immediately notice that the code does not compile any more. The tools will tell you that you cannot instantiate an abstract class. The reason is that you return objects of type ReguarLanguage<...> from your functions by value, thereby invoking the Curse of Object Slicing (look it up). The inability to instantiate an abstract class is just manifestation of Object Slicing.
In order to dispel the dreaded curse, you will have to do the Rite of Return by Pointer, and thereby invoke the lesser curse of Manual Memory Management (this is relatively easily defeated with a bit of Smart Pointer Magic these days).
But you are not only returning languages, you are also accepting them as arguments. Here you need a simpler incantation of Passing by Reference, which does not require any manual memory management. It looks like in your case you can implement the intersection and all the other things by using just the public interface of the "other" argument, so this simple change should suffice.
Oh, and don't forget the virtual destructor. Never forget the virtual destructor... only omit it if you know you don't need it, which is, if your class fits the OO paradigm, never.
If you look at your RegularLanguage class template, you may notice that it never use its template parameter. If this is the case, it can be safely de-templatized. No, wait, it does use the parameter — how else could it create e.g. a complement?
To put it simply, if you have a finite automaton that parses a language, the parser of the complement doesn't have to be a finite automaton. It may contain or reference a FA (or something else!) instead. So you may want to have a subclass of RegularLanguage called Complement that will contain a (polymorphic!) reference to another RegularLanguage — without even having to know what kind of parser is hidden inside that one.
Consider the following code:
#include <iostream>
#include <type_traits>
// Abstract base class
template<class Crtp>
class Base
{
// Lifecycle
public: // MARKER 1
Base(const int x) : _x(x) {}
protected: // MARKER 2
~Base() {}
// Functions
public:
int get() {return _x;}
Crtp& set(const int x) {_x = x; return static_cast<Crtp&>(*this);}
// Data members
protected:
int _x;
};
// Derived class
class Derived
: public Base<Derived>
{
// Lifecycle
public:
Derived(const int x) : Base<Derived>(x) {}
~Derived() {}
};
// Main
int main()
{
Derived d(5);
std::cout<<d.set(42).get()<<std::endl;
return 0;
}
If I want a public inheritance of Derived from Base, and if I don't want a virtual destructor in the base class, what would be the best keywords for the constructor (MARKER 1) and the destructor (MARKER 2) of Base to guarantee that nothing bad can happen ?
Whatever programming style you use, you can alwyas do something bad: even if you follow the best of the bestest guideline practice. That's something physical behind it (and relate to the impossibility to reduce the global entrophy)
That said, don't confuse "classic OOP" (a methodology) with C++ (a language), OOP inheritache (a relation) with C++ inheritance (an aggregation mechanism) and OOP polymorphism (a model) with C++ runtime and static polymorphism (a dispatching mechanism).
Although names sometime matches, the C++-things don't have to necessarily sevicing OOP-things.
Public inheritance from a base with some non-virtual methods is normal. and destructor is not special: just dont call delete on the CRTP base.
Unlike with classic OOP, a CRTP-base has different type for each of the deriveds, so having a "pointer to a base" is clueless since there is no "pointer to a common type". And hence the risk to call "delete pbase" is very limited.
The "protected-dtor paradigm" is valid only if you are programming OOP-inheritance using C++ inheritance for object managed (and deleted) though pointer-based polymorphism. If you are following other paradigms, those rules should not be treated in a literal way.
In your case, the proteced-dtor just deny you to create a Base<Derived> on the stack and to call delete on a Base*. Something you will never do, since Base with no "Dervied" has no sense to exist, and having a Base<Derived>* makes no sense since you can have just a Derived*, hence having both public ctor and dtor makes no particular mess.
But you can even do the opposite choice to have both ctor and dtor protected, since you will never construct a Base alone, since it always needs a Derived type to be known.
Because of the particular construction of CRTP, all the classical OOP stuff leads to a sort of "indifferent equilibrium", since there is no more the "dangerous usecase".
You can use them or not, but no particular bad-thing can happen. Not if you use object the way they had been designed to be used.
While your code works I find it odd to mark the destructor rather than the constructor as protected. Normally my reasoning would be that you want to prevent the programmer from accidentally creating a CRTP base object. It all comes down to the same of course, but this is hardly canonical code.
The only thing that your code prevents is the accidental deletion of a CRTP object via a base pointer – i.e. a case like this:
Base<Derived>* base = new Derived;
delete base;
But that is a highly artificial situation that won’t arise in real code since CRTP simply isn’t supposed to be used that way. The CRTP base is an implementation detail that should be completely hidden from the client code.
So my recipe for this situation would be:
Define a protected constructor.
Don’t define a destructor – or, if required for the CRTP semantic, define it as public (and non-virtual).
There's no problem, since the destructor is protected it means that client code can't delete a pointer to Base, so there's no problem with Base's destructor being non-virtual.
(This question should probably be answered with a reference to Stroustrup.)
It seems extremely useful to be able to request a pointer to the most derived class, as in the following:
class Base { ... };
class DerivedA { ... };
class DerivedB { ... };
class Processor
{
public:
void Do(Base* b) {...}
void Do(DerivedA* d) {...}
void Do(DerivedB* d) {...}
};
list<Base*> things;
Processor p;
for(list<Base*>::iterator i=things.begin(), e=things.end(); i!=e; ++i)
{
p.Do(CAST_TO_MOST_DERIVED_CLASS(*i));
}
But this mechanism isn't provided in c++. Why?
Update, Motivating Example:
Suppose instead of having Base and Derived and Processor, you have:
class Fruit
class Apple : public Fruit
class Orange: public Fruit
class Eater
{
void Eat(Fruit* f) { ... }
void Eat(Apple* f) { Wash(f); ... }
void Eat(Orange* f) { Peel(f); ... }
};
Eater me;
for each Fruit* f in Fruits
me.Eat(f);
But this is tricky to do in C++, requiring creative solutions like the visitor pattern. The question, then, is: Why is this tricky to do in C++, when something like "CAST_TO_MOST_DERIVED" would make it much simpler?
Update: Wikipedia Knows All
I think Pontus Gagge has a good answer. Add to it this bit from the Wikipedia entry on Multiple Dispatch:
"Stroustrup mentions that he liked the concept of Multi-methods in The Design and Evolution of C++ and considered implementing it in C++ but claims to have been unable to find an efficient sample implementation (comparable to virtual functions) and resolve some possible type ambiguity problems. He goes on to state that although the feature would still be nice to have, that it can be approximately implemented using double dispatch or a type based lookup table as outlined in the C/C++ example above so is a low priority feature for future language revisions."
For background, you can read a little summary about Multi-Methods, which would be better than a call like the one I mention, because they'd just work.
Probably because that's what virtual functions do for you instead. The implementation of the virtual function that is nearest the most-derived class will be called when you invoke it through a base class pointer or reference.
Firstly, C++ does allow you to request a pointer to a most derived class in numerical terms (i.e. just the numerical value of the address). This is what dynamic_cast to void* does.
Secondly, there's no way to obtain a pointer to the most derived class in therms of exact type of the most derived class. In C++ casts work with static types, and static type is a compile-time concept. Type-based function overloading is also a compile-time process. The exact most derived type is not known at compile-time in your case, which is why cannot cast to it and can't resolve overloading on it. The request to have such a cast makes no sense in the realm of C++ language.
What you are trying to implement (if I understood your intent correctly), is implemented by completely different means, not by a cast. Read about double dispatch, for one example.
Because the type of i is not determinable at compile time. Therefore the compiler would not know which function call to generate. C++ only supports one method of dynamic dispatch that is the virtual function mechanism.
What you are suggesting would be equivalent to a switch on the runtime type, calling one of the overloaded functions. As others have indicated, you should work with your inheritance hierarchy, and not against it: use virtuals in your class hierarchy instead of dispatching outside it.
That said, something like this could be useful for double dispatch, especially if you also have a hierarchy of Processors. But how would the compiler implement it?
First, you'd have to extract what you call 'the most overloaded type' at runtime. It can be done, but how would you deal with e.g. multiple inheritance and templates? Every feature in a language must interact well with other features -- and C++ has a great number of features!
Second, for your code example to work, you'd have to get the correct static overload based on the runtime type (which C++ does not allow as it is designed). Would you like this to follow the compile time lookup rules, especially with multiple parameters? Would you like this runtime dispatch to consider also the runtime type of your Processor hierarchy, and what overloads they have added? How much logic would you like the compiler to add automatically into your runtime dispatcher? How would you deal with invalid runtime types? Would users of the feature be aware of the cost and complexity of what looks like a simple cast and function call?
In all, I´d say the feature would be complex to implement, prone to errors both in implementation and usage, and useful only in rare cases.
It's called using a virtual function call. Pass the processor* into DerivedA/B's virtual method. Not the other way around.
There is no mechanism provided because it's totally unnecessary and redundant.
I swear, I fielded this exact question about a day or two ago.
In C++ overload resolution happens at compile time. Your example would require determining the real type of *i at runtime. For it to be done at runtime would require a runtime type check, and because C++ is a performance oriented language it purposefully avoids this cost. If you really wanted to do this (and I'd be curious to see a more realistic example) you could dynamic_cast to the most derived class, then if that fails to the second most derived class, and so on, but this requires knowing the class hierarchy up front. And knowing the full hierarchy up front maybe impossible -- if the DerivedB class is in a public header, it's possible another library uses it and has made an even more derived class.
You're looking for double dispatch. It can be done in C++, as shown at that link, but it's not pretty, and it basically involves using two virtual functions calling each other. If you can't modify some of the objects in your inheritance tree, you may not be able to use this technique either.
This is not possible in C++, but what you want to achieve is easily doable using the Visitor design pattern:
class Base
{
virtual void accept(BaseVisitor& visitor) { visitor.visit(this); }
};
class DerivedA
{
virtual void accept(BaseVisitor& visitor) { visitor.visit(this); }
};
class DerivedB
{
virtual void accept(BaseVisitor& visitor) { visitor.visit(this); }
};
class BaseVisitor
{
virtual void visit(Base* b) = 0;
virtual void visit(DerivedA* d) = 0;
virtual void visit(DerivedB* d) = 0;
};
class Processor : public BaseVisitor
{
virtual void visit(Base* b) { ... }
virtual void visit(DerivedA* d) { ... }
virtual void visit(DerivedB* d) { ... }
};
list<Base*> things;
Processor p;
for(list<Base*>::iterator i=things.begin(), e=things.end(); i!=e; ++i)
{
(*i)->visit(p);
}
Why doesn't C++ have it? Perhaps the creators never thought about it. Or perhaps they didn't consider it suitable or useful enough. Or perhaps there were problems with actually trying to do it in this language.
On that last possibility, here's a thought experiment:
Lets say the this feature exists so that the compiler will write code that examines the dynamic type pointed to and calls the appropriate overload. Now lets also say a separate portion of the code has class DerivedC : Base {...};. And say that the corresponding Processor::Do overload is not added.
Given all of that, what should the program do when it tries to choose the appropriate overload? This discrepancy cannot be caught at compile-time. Should it try to climb the class hierarchy to find a function that matches a base class? Should it throw a special exception? Should it just crash? Is there some other possibility? Is there actually any reasonable choice that the compiler could make on its own without knowing the intention of your code and class hierarchy?
Yes, writing such functionality yourself would be susceptible to the same problem, but there the programmer has total control to choose the behavior, not the compiler.
C++ interprets data in the context of the associated type. When you store an instance of DerivedA* or DerivedB* in a list, that associate type must necessarily be Base*. This means that the compiler itself can no longer determine that those are pointers to one of the subclasses rather than the base class. While in theory you could cast to a LESS derived class by looking at the associated type's inheritance, the information needed to do what you want simply isn't available at compile-time.
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.