Curiously Recurring Template Pattern (CRTP)

Share the Article

CRTP is a technique of achieving static polymorphism. And this technique is also called F-bound polymorphism and it is a form of F-bounded quantification. During the first glace, this technique looks like it is a replacement of dynamic polymorphism. However, there are scenarios where it is actually difficult to replace virtual functions. The article will discuss both advantages and disadvantages.

Contrarily, a CRTP class do NOT achieve polymorphic behavior by using inheritance. Instead, member in base specifically calls a member function in derived class.

Dynamic Polymorphism using virtual functions.

First lets revisit an example with, dynamic polymorphism instead. To illustrate, the class Vehicle is a base class and has a virtual function “drive”. Secondly, the derived class, Car will override the “drive” virtual function.

#include <iostream> using namespace std; class Vehicle { public: virtual void drive() = 0; }; class Car : public Vehicle { public: void drive() { cout << "Car::drive()" << endl; } }; int main() { Vehicle *v1 = new Car; v1->drive(); return 0; }

Since, v1 is pointing to Car object, therefore, compiler will generate code to call “drive” from the Car class. The output is:

Car::drive()

Same example with CRTP

To understand CRTP, following 2 points are important:

  1. Firstly, Base class is a template class
  2. Secondly, Derived class uses itself as template argument to instantiate the base.

The drive member of Vehicle base specifically, calls the drive member of template parameter type. This is because, the template parameter is a very special class which contains the real implementation. In the second place, the derived class knows this understanding of base class. Therefore, it offers itself to become template parameter for base.

#include <iostream> using namespace std; template<typename T> class Vehicle { public: void drive() { ((T*)this)->drive(); } }; class Car : public Vehicle<Car> { public: void drive() { cout << "Car::drive()" << endl; } }; int main() { Vehicle<Car> *v1 = new Car; v1->drive(); return 0; }

In the above example, v1 is a pointer to template initialization of base class (Vehicle<Car>). However, just like, polymorphism, this points to an object of derived class Car. Therefore, ultimately, drive function from derived class Car is called. And since, no virtual functions were involved, hence the runtime performance is faster.

Therefore, the output is:

Car::drive()

Problem with CRTP

Although CRTP ultimately, calls the member function in derived class. However, the problem is that Base class is actually a template class and not an actual class. Consequently, if there is a second Derived class, named Bus, then base class template initialization will be of different type. This is because, the Bus class will derive from a different specialization, i.e., Vehicle<Bus>

For Example,

class Bus : public Vehicle<Bus> { public: void drive() { cout << "Bus::drive()" << endl; } };

This means that there will 2 types of Base pointers.

//with CRTP Vehicle<Car> *v1 = new Car; Vehicle<Bus> *v2 = new Bus;

Clearly, this has become a different setup than what we had in virtual functions. Because, the dynamic polymorphism gave only one type of Base pointer. But now, with every derived class a different pointer type is available.

//With dynamic polymorphism Vehicle *v1 = new Car; Vehicle *v2 = new Bus;

Due to different kinds of It is not possible to store CRTP base class pointers in a vector.

int main() { Vehicle<Car> *v1 = new Car; Vehicle<Bus> *v2 = new Bus; auto vecObj = {v1, v2}; //This is wrong return 0; }

The compiler will generate following error:

crtp.cpp:40:26: error: unable to deduce 'std::initializer_list<auto>' from '{v1, v2}' 40 | auto vecObj = {v1, v2}; | ^ crtp.cpp:40:26: note: deduced conflicting types for parameter 'auto' ('Vehicle<Car>*' and 'Vehicle<Bus>*')

Workaround for vector problem

Workaround is to again fallback to the virtual functions. For example, there may be a top-level abstract class like, AbstractVehicle. Consequently, from this class the CRTP template class Vehicle shall derive.

class AbstractVehicle { public: virtual ~AbstractVehicle() = 0; }; AbstractVehicle::~AbstractVehicle() {} template<typename T> class Vehicle : public AbstractVehicle { }

The vector shall be of the type of AbstractVehicle

Vehicle<Car> *v1 = new Car; Vehicle<Bus> *v2 = new Bus; std::vector<AbstractVehicle*> vec = {v1, v2}; //Correct

Main Funda: CRTP is a special type of polymorphism which in few cases can replace the need of dynamic polymorphism

Related Topics:

What are dependent scope type in templates?
What is an implicit interface? 
Calling member functions in template base class
What is template meta programming (TMP)
How delete keyword can be used to filter polymorphism
Concept of Inline Functions
What is reference collapsing?
How std::forward( ) works?
How std::move() function works?
Smart Pointers: shared_ptr <T> 
Smart Pointers: unique_ptr<T>
What is move constructor ?
Understanding Constant Variables

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *