Jonathan Boccara's blog

An Implementation Helper For The Curiously Recurring Template Pattern

Published May 19, 2017 - 14 Comments

In this final episode of the series on the Curiously Recuring Template Pattern, let’s see an implementation that makes it easier to write CRTP classes.

In case you missed an episode in the series, here they are:

Getting rid of static_cast

Writing repeated static_casts in CRTP base classes quickly becomes cumbersome, as it does not add much meaning to the code:

template <typename T>
struct NumericalFunctions
{
    void scale(double multiplicator)
    {
        T& underlying = static_cast<T&>(*this);
        underlying.setValue(underlying.getValue() * multiplicator);
    }
    ...
};

It would be nice to factor out these static_casts. This can be achieved by forwarding the underlying type to a higher hierarchy level:

template <typename T>
struct crtp
{
    T& underlying() { return static_cast<T&>(*this); }
    T const& underlying() const { return static_cast<T const&>(*this); }
};

Plus it deals with the case where the underlying object is const, which we hadn’t mentioned yet.

This helper can be used the following way:

template <typename T>
struct NumericalFunctions : crtp<T>
{
    void scale(double multiplicator)
    {
        this->underlying().setValue(this->underlying().getValue() * multiplicator);
    }
    ...
};

Note that the static_cast is gone and a this-> appeared. Without it the code would not compile. Indeed, the compiler is not sure where underlying is declared. Even if it is declared in the template class crtp, in theory nothing guarantees that this template class won’t be specialized and rewritten on a particular type, that would not expose an underlying method. For that reason, names in template base classes are ignored in C++.

Using this-> is a way to include them back in the scope of functions considered to resolve the call. There are other ways to do it, although they are arguably not as adapted to this situation. In any case, you can read all about this topic in Effective C++ Item 43.

Anyway, the above code relieves you from writing the static_casts, which become really cumbersome when they are several of them.

All this works if you class only add one functionality via CRTP, but it stops working if there are more.

Adding several functionalities with CRTP

For the sake of the example let’s split our CRTP classes into two: one that scales values and one that squares them:

template <typename T>
struct Scale : crtp<T>
{
    void scale(double multiplicator)
    {
        this->underlying().setValue(this->underlying().getValue() * multiplicator);
    }
};

template <typename T>
struct Square : crtp<T>
{
    void square()
    {
        this->underlying().setValue(this->underlying().getValue() * this->underlying().getValue());
    }
};

And add these two functionalities to the Sensitivity class:

class Sensitivity : public Scale<Sensitivity>, public Square<Sensitivity>
{
public:
    double getValue() const { return value_; }
    void setValue(double value) { value_ = value; }

private:
    double value_;
};

This looks ok at first glance but does not compile as soon as we call a method of either one of the base class!

error: 'crtp<Sensitivity>' is an ambiguous base of 'Sensitivity'

The reason is that we have a diamond inheritance here:

I tried to solve this with virtual inheritance at first, but quickly gave this up because I didn’t find how to do it simply and without impacting the clients of the crtp class. If you have a suggestion, please, voice it!

Another approach is to steer away from the diamond inheritance (which sounds like a good idea), by having every functionality (scale, square) inherit from its own crtp class. And this can be achieved by… CRTP!

Indeed, we can add a template parameter to the crtp class, corresponding to the base class. Note the addition of the crtpType template parameter.

EDIT: as suggested by Daniel Houck in the comments section, the private-constructor-and-friend-with-derived technique should also be applied on this template template parameter here, because it forces Scale to inherit from the right crtp. Note that it doesn’t force Sensitivity to inherit from the right CRTP though, so the friend and private constructor are still needed in Scale and Square (thanks to Amos Bird for pointing this out).

template <typename T, template<typename> class crtpType>
struct crtp
{
    T& underlying() { return static_cast<T&>(*this); }
    T const& underlying() const { return static_cast<T const&>(*this); }
private:
    crtp(){}
    friend crtpType<T>;
};

Note that the template parameter is not just a typename, but rather a template<typename> class. This simply means that the parameter is not just a type, bu rather a template itself, templated over a type whose name is omitted. For example crtpType can be Scale.

This parameter is here to differentiate types only, and is not used in the implementation of crtp (except for the technical check in the friend declaration). Such an unused template parameter is called a “phantom type” (or to be more accurate here we could call it a “phantom template”).

The class hierarchy now looks like the following:

and we’re good to go.

A CRTP on a CRTP. Templates are so much fun.

Related articles:

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

Comments are closed