Jonathan Boccara's blog

How to Emulate The “super” Keyword In C++

Published December 26, 2017 - 11 Comments

super c++

Daily C++

 

[A Russian translation of this article is available on howtorecover.me – courtesy of Vlad Brown]

 

A derived class sometimes needs to call the code of its base class and name it explicitly.

But for bases classes with a long name, repeating it in the body of the derived class adds a lot of clutter to it. And C++ doesn’t have a super or base keyword to designate “the base class”, like C# and Java do.

One reason for this is that C++ supports multiple inheritance, which would make such a keyword ambiguous. But on the other hand, multiple inheritance is not used so often in C++. So how can a derived class designate its base in the case of single inheritance?

Fortunately there are ways to do this, to make the code of the derived class more expressive.

Repeating the base class’s name?

Here are two situations where a derived class needs to invoke its base’s name.

Calling a constructor of the base class

If a constructor of the derived class needs to call the default constructor of the base type (the one that takes no argument) then no need to write it: the compiler does it for us.

But if the constructor takes some parameters, then we need to call it explicitly:

class Shape
{
public:
    explicit Shape(Color color);
};

class Oval : public Shape
{
public:
    Oval(Color color) : Shape(color)
    {
        ...
    }
};

Calling the implementation of the base class for a virtual method

class Shape
{
public:
    virtual void draw() const override
    {
        // base class implementation for draw
    }
};

class Oval : public Shape
{
public:
    void draw() const override
    {
        Shape::draw(); // calls the base class implementation
        ...

In theory, you could also need to call the implementation of a non-virtual base class method. But if you have to write the name of the base class to it, it means that you have a non-virtual method of the derived class that has the same name as a non-virtual method of the base class. And as explained in Item 36 of Effective C++, you shouldn’t do that because the derived class would hide the method name of the base class, leading to surprising results.

In C++, virtual functions (and even pure virtual ones) can have an implementation in the base class, where code can be factored. The above derived class calls this implementation by naming the base class explicitly.

When it becomes a problem

The above cases use short names for the base class. But what if the base class was a template, and located in a nested namespace different from the derived type’s namespace?

class Oval : public NA::NB::NC::Shape<FirstParameter, SecondParameter, ThirdParameter>
{
public:
    Oval(Color color) : NA::NB::NC::Shape<FirstParameter, SecondParameter, ThirdParameter>(color){}
    
    void draw() const override
    {
        NA::NB::NC::Shape<FirstParameter, SecondParameter, ThirdParameter>::draw();
    }
};

Ew. That’s a lot of code, and which doesn’t express anything more that the previous code snippets. It just has the same base name repeated over and over.

This is where we start eyeing the other languages with envy. But not yet! C++ has what we need to remove all that redundant information.

You don’t have to write what the compiler already knows

One of the goals that the language is steering towards is to relieve the developer from the work that the compiler can do on its own. This is well illustrated with the auto keyword in C++11 and template deduction in constructors in C++17.

And even since C++98, the compiler could figure out that when the derived class talks about “Shape“, it is its base class that it means. So the above is exactly equivalent to:

class Oval : public NA::NB::NC::Shape<FirstParameter, SecondParameter, ThirdParameter>
{
public:
    Oval(Color color) : Shape(color){}
    
    void draw() const override
    {
        Shape::draw();
    }
};

This works unless there is an ambiguity, like if the derived class inherits from two different specializations of the template base class. Or from two classes with the same name but in different namespaces.

But when there isn’t such a alignment of planets as that, using the base class’s name without its template parameters or namespaces works.

Using an alias

Still, if you want to abstract the name of the base class behind a dedidcated word, such as super or base, this is possible by using a typedef or a using declaration.

One way to go about this is to add the using declaration at the beginning of the derived class (private) section:

class Oval : public NA::NB::NC::Shape<FirstParameter, SecondParameter, ThirdParameter>
{
    using base_type = Shape;
public:
    Oval(Color color) : base_type(color){}
    
    void draw() const override
    {
        base_type::draw();
    }
};

But there is another place for the using declaration: in the base class itself. I first saw this in the implementation of the Boost spirit library.

If you opt for doing this, it makes sense to put the declaration in the protected section of the base class. This way, it cannot be mixed up as the base class of the base class itself:

template<typename T1, typename T2, typename T3>
class Shape
{
public:
    explicit Shape(Color){}
    virtual void draw() const
    {
        // base class implementation for draw
    }
protected:
    using base_type = Shape;
};

And the derived classes can use base_type in their implementations without having to write a using declaration at their level.

This expresses the message that the class is designed to be inherited from. But the virtual methods already sent this message anyway.

Of course, like the super and base keywords of the other languages, you can only use the using declaration in the base class with single inheritance, otherwise there is an ambiguity in the derived class. But, not unlike Bridget Jones, inheritances are single most of the time anyway (thanks to my brother-in-law Joachim for this astute closing joke).

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed