230

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • Awesome! Exactly what I was looking for. Don't think I've ever needed to use this technique until now. Excellent solution for my cross-platform code. – AlanKley Oct 22 '08 at 14:49
  • 8
    For me `super` looks like `Java` and it is nothing bad, but... But `C++` supports multiple inheritance. – ST3 Aug 07 '13 at 14:48
  • 2
    @user2623967 : Right. In the case of simple inheritance, one "super" is enough. Now, if you have multiple inheritance, having "superA", "superB", etc. is a good solution: You WANT to call the method from one implementation or another, so you must TELL what implementation you want. Using "super"-like typedef enables you to provide an easy accessible/writable name (instead of, say, writing `MyFirstBase>` everywhere) – paercebal Aug 08 '13 at 15:24
  • Note that when inheriting from a template, you don't need to include the template arguments when referencing to it. For example: `template struct Foo {...void bar() {...} ...}; struct Foo2: Foo { void bar2() { ... Foo::bar(); ...} };` This worked to me with gcc 9.1 --std=c++1y (c++14). – Thanasis Papoutsidakis Sep 13 '14 at 13:37
  • 1
    ...Um, correction. It seems to work in any c++ standard, not just 14. – Thanasis Papoutsidakis Sep 13 '14 at 13:45

19 Answers19

179

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.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
  • 2
    I remember three features that were not accepted discussed in D&E. This is the first (look up "Michael Tiemann" in the index to find the story), the two week rule is the second (look up "two week rule" in the index), and the third was named parameters (look up "named arguments" in the index). – Max Lybbert Oct 08 '08 at 17:49
  • 14
    There is a major flaw in the `typedef` technique: it does not respect DRY. The only way around would be to use ugly macros to declare classes. When you inherit, the base could be a long multi parameter template class, or worse. (e.g. multi classes) you would have to rewrite all of that a second time. Finally, I see a big issue with template bases which have template class arguments. In this case the super is a template (and not an instanciation of a template). Which cannot be typedefed. Even in C++11 you need `using` for this case. – v.oddou May 28 '15 at 08:54
  • @v.oddou Isn't the typedef exactly there to *avoid* repeating yourself in every virtual function? – c z Aug 27 '21 at 09:48
  • 1
    @cz Yes, it is better than repeating the type name every time. But you still need to repeat the type name once: you have it first in the class declaration and second when defining the typedef. – John Gowers Mar 17 '22 at 11:48
113

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.

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • 2
    As for chaining super::super, as I mentionned in the question, I have still to find an interesting use to that. For now, I only see it as a hack, but it was worth mentioning, if only for the differences with Java (where you can't chain "super"). – paercebal Oct 08 '08 at 07:50
  • how do I use this now to call parent class methods? – mLstudent33 May 18 '20 at 03:04
  • does it mean I have to declare all Base class methods as `virtual` as shown here: http://www.martinbroadhurst.com/typedef-super.html – mLstudent33 May 18 '20 at 03:28
  • better=> "using inherited_t = MyBase;", but you still got duplicate code and still can't avoid the error from ctor delegation. – Jay Yang Aug 28 '20 at 05:02
39

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

Kristopher Johnson
  • 81,409
  • 55
  • 245
  • 302
23

FWIW Microsoft has added an extension for __super in their compiler.

krujos
  • 272
  • 2
  • 7
  • 11
    I'm working on a windows app, and love the __super extension. It saddens me that the standards committee rejected it in favor of the typedef trick mentioned here, because while this typedef trick is good, it requires more maintenance than a compiler keyword when you change the inheritence hierarchy, and handles multiple inheritence correctly (without requiring two typedefs like super1 and super2). In short, I agree with the other commentor that the MS extension is VERY useful, and anybody using Visual Studio exclusively should strongly consider using it. – Brian Jan 10 '12 at 23:18
15

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.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
15

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.

Colin Jensen
  • 3,739
  • 1
  • 20
  • 17
14

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.

paperjam
  • 8,321
  • 12
  • 53
  • 79
  • 4
    `public` alias is a downside, as you're open to the bug mentioned in [Roddy's](http://stackoverflow.com/a/180634/264047) and [Kristopher's](http://stackoverflow.com/a/180627/264047) answers (e.g. you can (by mistake) derive from `Derived` instead of `MakeAlias`) – Alexander Malakhov Mar 25 '13 at 10:35
  • 3
    You also don't have access to base class constructors in your initializer lists. (This can be compensated for in C++11 using inheriting constructors in `MakeAlias` or perfect forwarding, but it does require you to refer to the `MakeAlias` constructors rather than just referring to `C`'s constructors directly.) – Adam H. Peterson Nov 25 '15 at 21:57
  • not a good idea if you'll be using delegation in derived ctor. for example: "Derived(): Base() {}" causes compile error – Jay Yang Aug 28 '20 at 04:57
11

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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
4

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)

James Hopkin
  • 13,797
  • 1
  • 42
  • 71
  • 1
    You're right. I found the reference here. http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/iterator_adaptor.html – paercebal Oct 07 '08 at 22:04
4

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.

Toji
  • 33,927
  • 22
  • 105
  • 115
  • 4
    As a counter-point, Perl has both multiple inheritance *and* SUPER. There is some confusion, but the algorithm that the VM goes through to find something is clearly documented. That said, I rarely see MI used where multiple base classes offer the same methods where confusion could arise anyway. – Tanktalus Oct 07 '08 at 22:10
  • 1
    Microsoft Visual Studio implements the __super keyword for C++ whether or not it's for CLI. If only one of the inherited classes offers a method of the correct signature (the most common case, as mentioned by Tanktalus) then it just picks the only valid choice. If two or more inherited classes provide a function match, it doesn't work and requires you to be explicit. – Brian Jan 10 '12 at 23:23
3

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++

jdkoftinoff
  • 2,391
  • 1
  • 17
  • 17
2

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.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
2

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.

Kevin
  • 424
  • 3
  • 10
1

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.

Matt Dillard
  • 14,677
  • 7
  • 51
  • 61
1

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.

Scott Langham
  • 58,735
  • 39
  • 131
  • 204
1

I use the __super keyword. But it's Microsoft specific:

http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx

Mark Ingram
  • 71,849
  • 51
  • 176
  • 230
1

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?

  1. super means, calling the last implementor of a method (not base method)
  2. 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:

  1. polymorphic call: calls downward
  2. 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.

metablaster
  • 1,958
  • 12
  • 26
1

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.

enter image description here

So which superclass is "D"'s superclass?? "Both" cannot be solution because "super" keyword is pointer.

  • 2
    More or less: The problem here is multiple inheritance, not the very specific case or DDD. In your example, you don't even need A for the "super" to be ambiguous. Having D inheriting from B and C is enough. Thanks, – paercebal Jan 09 '22 at 15:40
0

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.

Zhro
  • 2,546
  • 2
  • 29
  • 39