Author image

Curiously Recurring Template Pattern


Difficulty:
1/5


Curiously Recurring Template Pattern, or CRTP for short, is essentially a C++ idiom. It is also known as Static Polymorphism or Simulated polymorphism.

Here's the differences between (actual) Dynamic polymorphism and Static polymorphism (CRTP):

Requirements for dynamic polymorphism:

  1. Is-a relationship between base and derived class.
  2. Base class defines a 'generic' algorithm that is used by derived class.
  3. The 'generic' algorithm is customized by the derived class.

Requirements for static polymorphism:

  1. 'Is-a' relationship between base and derived class.
  2. Base class defines a 'generic' algorithm that is used by derived class.
  3. The 'generic' algorithm is customized by the derived class.
  4. This method is pure virtual. The derived method implements it.

Benefits

  • CRTP is simulating dynamic polymorphism without any additional runtime penalty. To avoid run time costs when the specialized method is invariant of the one in the base method then this resolution can be achieved at compile time using templated code. Templates do away with run-time virtual-table lookups.
  • The client is unaware of any changes - hestill sees and uses the polymorphic function as an actual polymorphic function, having no clue about its templatedness

Disadvantages

  • space heavy - larger image size due to templates
  • Virtual methods are more powerful in the sense that, unlike the CRTP, they are able to discover the implementation of an interface at each runtime call. This is dynamic polymorphism. Each object* can change the implementation by newing a new OtherDerivedClass(); and then running a function on it. With CRTP we get static polymorphism; ie B is A, but it can't change to C (which also inherits from A) during runtime. Thus the cost is flexibility.

Truth be told, the second point made above is a significant weakness of CRTP, which is why you should use it only when performance is critical and you are absolutely certain you don't need to change behavior at runtime - which kind of breaks the need of virtual functions in the first place, but I'm sure you can find a use for it. : )

Design

  • base class is a (concrete) template class
  • derived class is a non-template class which derives from the base class with template type argument being its own type
  • only derived class objects are to be created
  • base class defines an interface like method which calls the appropriate implementation method of a derived class based on the type of the calling object it receives as argument. The interface and the implementation method need not have the same signature
  • To accomplish this at compile time the interface method calls the implementation method like so: static_cast<T*>( this )->implementation( args... ).

Blueprint:

template<class Derived>
struct Base
{
    void interface()
    {
        // ...
        static_cast<Derived*>( this )->implementation();
// or if you prefer references: // static_cast<Derived const&>( *this ).implementation(); // ... } }; struct Derived1 : public Base<Derived1> { void implementation() { // ... } }; struct Derived2 : public Base<Derived2> { void implementation() { // ... } };

As usual, I used Windows and Visual Studio to build the project.

Github

Github repository link.


0 likes