243

Without referring to a book, can anyone please provide a good explanation for CRTP with a code example?

TankorSmash
  • 12,186
  • 6
  • 68
  • 106
Alok Save
  • 202,538
  • 53
  • 430
  • 533

6 Answers6

333

In short, CRTP is when a class A has a base class which is a template specialization for the class A itself. E.g.

template <class T> 
class X{...};
class A : public X<A> {...};

It is curiously recurring, isn't it? :)

Now, what does this give you? This actually gives the X template the ability to be a base class for its specializations.

For example, you could make a generic singleton class (simplified version) like this

template <class ActualClass> 
class Singleton
{
   public:
     static ActualClass& GetInstance()
     {
       if(p == nullptr)
         p = new ActualClass;
       return *p; 
     }

   protected:
     static ActualClass* p;
   private:
     Singleton(){}
     Singleton(Singleton const &);
     Singleton& operator = (Singleton const &); 
};
template <class T>
T* Singleton<T>::p = nullptr;

Now, in order to make an arbitrary class A a singleton you should do this

class A: public Singleton<A>
{
   //Rest of functionality for class A
};

So you see? The singleton template assumes that its specialization for any type X will be inherited from singleton<X> and thus will have all its (public, protected) members accessible, including the GetInstance! There are other useful uses of CRTP. For example, if you want to count all instances that currently exist for your class, but want to encapsulate this logic in a separate template (the idea for a concrete class is quite simple - have a static variable, increment in ctors, decrement in dtors). Try to do it as an exercise!

Yet another useful example, for Boost (I am not sure how they have implemented it, but CRTP will do too). Imagine you want to provide only operator < for your classes but automatically operator == for them!

you could do it like this:

template<class Derived>
class Equality
{
};

template <class Derived>
bool operator == (Equality<Derived> const& op1, Equality<Derived> const & op2)
{
    Derived const& d1 = static_cast<Derived const&>(op1);//you assume this works     
    //because you know that the dynamic type will actually be your template parameter.
    //wonderful, isn't it?
    Derived const& d2 = static_cast<Derived const&>(op2); 
    return !(d1 < d2) && !(d2 < d1);//assuming derived has operator <
}

Now you can use it like this

struct Apple:public Equality<Apple> 
{
    int size;
};

bool operator < (Apple const & a1, Apple const& a2)
{
    return a1.size < a2.size;
}

Now, you haven't provided explicitly operator == for Apple? But you have it! You can write

int main()
{
    Apple a1;
    Apple a2; 

    a1.size = 10;
    a2.size = 10;
    if(a1 == a2) //the compiler won't complain! 
    {
    }
}

This could seem that you would write less if you just wrote operator == for Apple, but imagine that the Equality template would provide not only == but >, >=, <= etc. And you could use these definitions for multiple classes, reusing the code!

CRTP is a wonderful thing :) HTH

Juan Carlos Ramirez
  • 2,054
  • 1
  • 7
  • 22
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • @Armen Tsirunyan: That is a nice answer! thanks it does help :) In the 2nd example you provided, the templated class can only be used as a base class for CRTP, since the == assumption. In general in STL, templated classes are implemented and specifically marked for usage in CRTP or are implemented independently without any dependeny/assumption like the 2nd example? – Alok Save Nov 13 '10 at 16:31
  • @Als: No, STL containers are not designed with CRTP in mind. All their operators are independently provided. – Armen Tsirunyan Nov 13 '10 at 16:34
  • Another example that comes from the real world is the CComBaseClass (?) Object from microsoft – John Dibling Nov 13 '10 at 17:23
  • 76
    This post doesn't advocate singleton as a good programing pattern.it simply uses it as an illustration that can be commonly understood.imo the-1 is unwarranted – John Dibling Nov 13 '10 at 17:26
  • 3
    @Armen: The answer explains CRTP in a way that can be understood clearly, its a nice answer, thanks for such a nice answer. – Alok Save Nov 16 '10 at 15:02
  • 1
    @Armen: thanks for this great explanation. I was sort of t getting CRTP before, but the equality example has been illuminating! +1 – Paul Apr 08 '11 at 09:31
  • 1
    Yet another example of using CRTP is when you need a non-copyable class: template class NonCopyable { protected: NonCopyable(){} ~NonCopyable(){} private: NonCopyable(const NonCopyable&); NonCopyable& operator=(const NonCopyable&); }; Then you use noncopyable as below: class Mutex : private NonCopyable { public: void Lock(){} void UnLock(){} }; – Viren Apr 21 '14 at 03:48
  • I love your example. Its very good at showing what CRTP can do. Unfortunately the code you have written will not actually produce a true singleton, since class A public constructor is still available. Therefore users can still produce multiple instances of A. If you want to change this to a true singleton, add a private constructor to A, move the constructors of singleton to protected, add static factory method to A. Move p = new ActualClass; to the static factory method in A and make a call to the factory method from inside singleton::getinstance(). Its a real pain but it works. – Zachary Kraus Aug 27 '14 at 01:38
  • 4
    @Puppy: Singleton is not terrible. It is by far overused by below average programmers when other approaches would be more appropriate, but that most of its usages are terrible doesn't make the pattern itself terrible. There are cases where singleton is the best option, although those are rare. – Kaiserludi Jul 28 '15 at 18:25
  • Cool. Suppose now that I want to endow `class A` with multiple properties, for example I want `A` to be a singleton and to have `operator==`. Wouldn't the suggested strategy lead me to multiple inheritance, with all the caveats associated with it? – Michael Jan 04 '17 at 23:27
  • I think the terminology **specialization** here should be replaced by **instantiation**. [Welcome to see my answer for the difference between these two concepts](https://stackoverflow.com/a/48222446/6243726). – Francis Jan 23 '18 at 06:51
  • What will happen if two classes derive from the same template base specialization? – Maestro Dec 17 '18 at 15:24
  • 1
    The example of singleton is good. But it has compiling error. Chang the access of `Singleton()` and other 3 functions to be `protected` and Change access of `ActualClass* p` to be `private`. – where23 Jul 10 '19 at 02:31
  • can we consider CRTP as a compiled mixin provided through a particular interface? – VladiC4T Jun 05 '21 at 08:40
  • @VladiC4T one way of implementing a mixin in C++ is for it to be a CRT, yes – Caleth Jul 23 '21 at 12:59
  • I'm a bit confused about how he implemented it as a singleton. Following the same code, I am unable to create a new instance of a using ```A bob = A::GetInstance();```. how would you go about doing this? – MmMm SsSs Dec 14 '22 at 01:25
64

Here you can see a great example. If you use virtual method the program will know what execute in runtime. Implementing CRTP the compiler is which decide in compile time!!! This is a great performance!

template <class T>
class Writer
{
  public:
    Writer()  { }
    ~Writer()  { }

    void write(const char* str) const
    {
      static_cast<const T*>(this)->writeImpl(str); //here the magic is!!!
    }
};


class FileWriter : public Writer<FileWriter>
{
  public:
    FileWriter(FILE* aFile) { mFile = aFile; }
    ~FileWriter() { fclose(mFile); }

    //here comes the implementation of the write method on the subclass
    void writeImpl(const char* str) const
    {
       fprintf(mFile, "%s\n", str);
    }

  private:
    FILE* mFile;
};


class ConsoleWriter : public Writer<ConsoleWriter>
{
  public:
    ConsoleWriter() { }
    ~ConsoleWriter() { }

    void writeImpl(const char* str) const
    {
      printf("%s\n", str);
    }
};
Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
GutiMac
  • 2,402
  • 1
  • 20
  • 27
  • 1
    Couldn't you do this by defining `virtual void write(const char* str) const = 0;`? Though to be fair, this technique seems super helpful when `write` is doing other work. – atlex2 Aug 09 '16 at 14:44
  • 31
    Using a pure virtual method you are solving the inheritance in runtime instead of compile time. CRTP is used to solve this in compile time so the execution will be faster. – GutiMac Aug 10 '16 at 07:31
  • 3
    Try making a plain function that expects an abstract Writer: you can't do it because there is no class named Writer anywhere, so where's your polymorphism exactly? This is not equivalent with virtual functions at all and it's far less useful. –  Feb 11 '19 at 11:46
44

CRTP is a technique to implement compile-time polymorphism. Here's a very simple example. In the below example, ProcessFoo() is working with Base class interface and Base::Foo invokes the derived object's foo() method, which is what you aim to do with virtual methods.

http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e

template <typename T>
struct Base {
  void foo() {
    (static_cast<T*>(this))->foo();
  }
};

struct Derived : public Base<Derived> {
  void foo() {
    cout << "derived foo" << endl;
  }
};

struct AnotherDerived : public Base<AnotherDerived> {
  void foo() {
    cout << "AnotherDerived foo" << endl;
  }
};

template<typename T>
void ProcessFoo(Base<T>* b) {
  b->foo();
}


int main()
{
    Derived d1;
    AnotherDerived d2;
    ProcessFoo(&d1);
    ProcessFoo(&d2);
    return 0;
}

Output:

derived foo
AnotherDerived foo
Chenna V
  • 10,185
  • 11
  • 77
  • 104
  • 2
    It might also be worth it in this example to add an example of how to implement a default foo() in the Base class that will be called if no Derived has implemented it. AKA change foo in the Base to some other name(e.g. caller()), add a new function foo() to the Base that cout's "Base". Then call caller() inside of ProcessFoo – wizurd Apr 11 '18 at 15:15
  • @wizurd This example is more to illustrate a pure virtual base class function i.e. we enforce that `foo()` is implemented by the derived class. – Chenna V Jun 04 '18 at 17:01
  • 8
    This is my favourite answer, since it also shows why this pattern is useful with the `ProcessFoo()` function. – Pietro Sep 11 '18 at 17:21
  • 4
    I do not get the point of this code, because with `void ProcessFoo(T* b)` and without having Derived and AnotherDerived actually derived it would still work. IMHO it would be more interesting if ProcessFoo did not make use of templates somehow. – Gabriel Devillers Jun 04 '20 at 15:32
  • 5
    @GabrielDevillers Firstly, the templatized `ProcessFoo()` will work with any type that implements the interface i.e. in this case the input type T should have a method called `foo()`. Second, in order to get a non-templatized `ProcessFoo` to work with multiple types, you would likely end up using RTTI which is what we want to avoid. Moreover, templatized version provides you compilation time check on the interface. – Chenna V Jun 12 '20 at 00:13
  • 1
    Many thanks!. This is the best explanation. Simple short example. Similar to this https://en.cppreference.com/w/cpp/language/crtp – Paul Nov 02 '21 at 11:40
  • 1
    Out of all the explanations for CRTP on this post, this one was not only the easiest to understand, but the fastest to put everything together in a very concrete and simple, short example. Thank you so much! – rayryeng Jun 24 '22 at 15:19
13

This is not a direct answer, but rather an example of how CRTP can be useful.


A good concrete example of CRTP is std::enable_shared_from_this from C++11:

[util.smartptr.enab]/1

A class T can inherit from enable_­shared_­from_­this<T> to inherit the shared_­from_­this member functions that obtain a shared_­ptr instance pointing to *this.

That is, inheriting from std::enable_shared_from_this makes it possible to get a shared (or weak) pointer to your instance without access to it (e.g. from a member function where you only know about *this).

It's useful when you need to give a std::shared_ptr but you only have access to *this:

struct Node;

void process_node(const std::shared_ptr<Node> &);

struct Node : std::enable_shared_from_this<Node> // CRTP
{
    std::weak_ptr<Node> parent;
    std::vector<std::shared_ptr<Node>> children;

    void add_child(std::shared_ptr<Node> child)
    {
        process_node(shared_from_this()); // Shouldn't pass `this` directly.
        child->parent = weak_from_this(); // Ditto.
        children.push_back(std::move(child));
    }
};

The reason you can't just pass this directly instead of shared_from_this() is that it would break the ownership mechanism:

struct S
{
    std::shared_ptr<S> get_shared() const { return std::shared_ptr<S>(this); }
};

// Both shared_ptr think they're the only owner of S.
// This invokes UB (double-free).
std::shared_ptr<S> s1 = std::make_shared<S>();
std::shared_ptr<S> s2 = s1->get_shared();
assert(s2.use_count() == 1);
Mário Feroldi
  • 3,463
  • 2
  • 24
  • 49
3

Just as note:

CRTP could be used to implement static polymorphism(which like dynamic polymorphism but without virtual function pointer table).

#pragma once
#include <iostream>
template <typename T>
class Base
{
    public:
        void method() {
            static_cast<T*>(this)->method();
        }
};

class Derived1 : public Base<Derived1>
{
    public:
        void method() {
            std::cout << "Derived1 method" << std::endl;
        }
};


class Derived2 : public Base<Derived2>
{
    public:
        void method() {
            std::cout << "Derived2 method" << std::endl;
        }
};


#include "crtp.h"
int main()
{
    Derived1 d1;
    Derived2 d2;
    d1.method();
    d2.method();
    return 0;
}

The output would be :

Derived1 method
Derived2 method
Jichao
  • 40,341
  • 47
  • 125
  • 198
  • I think this would break down as soon as you use Base with a derived that is also run time polymorphic because the static cast of the pointer to base would not result in a correct pointer to derived. – odinthenerd Dec 06 '13 at 08:42
  • @PorkyBrain: I cannot understand.. could you give a real-world example? – Jichao Dec 06 '13 at 08:48
  • 1
    sorry my bad, static_cast takes care of the change. If you want to see the corner case anyway even though it does not cause error see here: http://ideone.com/LPkktf – odinthenerd Dec 06 '13 at 09:01
  • understood, static_cast moved up-cast the object. – Jichao Dec 06 '13 at 15:43
  • 33
    Bad example. This code could be done with no `vtable`s without using CRTP. What `vtable`s truly provide is using the base class (pointer or reference) to call derived methods. You should show how it is done with CRTP here. – Etherealone Feb 06 '14 at 17:37
  • 19
    In your example, `Base<>::method ()` isn't even called, nor do you use polymorphism anywhere. – MikeMB Mar 12 '15 at 09:14
  • 1
    @Jichao, according to @MikeMB 's note, you should call `methodImpl` in the `method` of `Base` and in derived classes name `methodImpl` instead of `method` – Ivan Kush Sep 17 '16 at 17:20
  • 1
    if you use similar method() then its statically bound and you don't need the common base class. Because anyway you couldn't use it polymorphically through base class pointer or ref. So the code should look like this: #include template struct Writer { void write() { static_cast(this)->writeImpl(); } }; struct Derived1 : public Writer { void writeImpl() { std::cout << "D1"; } }; struct Derived2 : public Writer { void writeImpl() { std::cout << "DER2"; } }; – barney Jun 02 '17 at 20:02
  • CRTP _can_ be used to implement static polymorphism, but static polymorphism is _not_ like dynamic polymprphism without vtables. If it were so, compilers would've optimized vtables away a long time ago. With CRTP, if you pass a Derived object to a function expecting Base, polymorphism _will not work_, which defeats the purpose. –  Feb 11 '19 at 11:37
1

Another good example of using CRTP can be an implementation of observer design pattern. A small example can be constructed like this.

Suppose you have a class date and you have some listener classes like date_drawer, date_reminder, etc.. The listener classes (observers) should be notified by the subject class date (observable) whenever a date change is done so that they can do their job (draw a date in some format, remind for a specific date, etc.). What you can do is to have two parametrized base classes observer and observable from which you should derive your date and observer classes (date_drawer in our case). For the observer design pattern implementation refer to the classic books like GOF. Here we only need to highlight the use of CRTP. Let's look at it. In our draft implementation observer base class has one pure virtual method which should be called by the observable class whenever a state change occurred, let's call this method state_changed. Let's look at the code of this small abstract base class.

template <typename T>
struct observer
{
    virtual void state_changed(T*, variant<string, int, bool>) = 0;
    virtual ~observer() {}
};

Here, the main parameter that we should focus on is the first argument T*, which is going to be the object for which a state was changed. The second parameter is going to be the field that was changed, it can be anything, even you can omit it, that's not the problem of our topic (in this case it's a std::variant of 3 fields). The second base class is

template <typename T>
class observable
{
    vector<unique_ptr<observer<T>>> observers;
protected:
    void notify_observers(T* changed_obj, variant<string, int, bool> changed_state)
    {
        for (unique_ptr<observer<T>>& o : observers)
        {
            o->state_changed(changed_obj, changed_state);
        }
    }
public:
    void subscribe_observer(unique_ptr<observer<T>> o)
    {
        observers.push_back(move(o));
    }
    void unsubscribe_observer(unique_ptr<observer<T>> o)
    {

    }
};

which is also a parametric class that depends on the type T* and that's the same object that is passed to the state_changed function inside the notify_observers function. Remains only to introduce the actual subject class date and observer class date_drawer. Here the CRTP pattern is used, we derive the date observable class from observable<date>: class date : public observable<date>.

class date : public observable<date>
{
    string date_;
    int code;
    bool is_bank_holiday;

public:
    void set_date_properties(int code_ = 0, bool is_bank_holiday_ = false)
    {
        code = code_;
        is_bank_holiday = is_bank_holiday_;
        //...
        notify_observers(this, code);
        notify_observers(this, is_bank_holiday);
    }

    void set_date(const string& new_date, int code_ = 0, bool is_bank_holiday_ = false) 
    { 
        date_ = new_date; 
        //...
        notify_observers(this, new_date);
    }
    string get_date() const { return date_; }
};

class date_drawer : public observer<date>
{
public:
    void state_changed(date* c, variant<string, int, bool> state) override
    {
        visit([c](const auto& x) {cout << "date_drawer notified, new state is " << x << ", new date is " << c->get_date() << endl; }, state);
    }
};

Let's write some client code:

date c;
c.subscribe_observer(make_unique<date_drawer>());
c.set_date("27.01.2022");
c.set_date_properties(7, true);

the output of this test program will be.

date_drawer notified, new state is 27.01.2022, new date is 27.01.2022
date_drawer notified, new state is 7, new date is 27.01.2022
date_drawer notified, new state is 1, new date is 27.01.2022

Note that using CRTP and passing this to the notify notify_observers function whenever a state change occurred (set_date_properties and set_date here). Allowed us to use date* when overriding void state_changed(date* c, variant<string, int, bool> state) pure virtual function in the actual date_drawer observer class, hence we have date* c inside it (not observable*) and for example we can call a non-virtual function of date* (get_date in our case) inside the state_changed function. We could of refrain from wanting to use CRTP and hence not parametrizing the observer design pattern implementation and use observable base class pointer everywhere. This way we could have the same effect, but in this case whenever we want to use the derived class pointer (even though not very recomendeed) we should use dynamic_cast downcasting which has some runtime overhead.