Author image

Curiously Recurring Template Pattern


Difficulty:
1/5


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

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. But, to avoid run time costs:
  5. 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 and thus does away with run-time virtual-table lookups. Then the pattern is also known as 'Curiously recurring template' pattern. It is simulating dynamic polymorphism without any performance penalty.

Benefits

  • All benefits of dynamic (actual) polymorphism without additional performance cost
  • The client still sees and uses the polymorphic function as an actual polymorphic function, having no idea about its templatedness

Disadvantages

  • larger image size due to templatized code
  • 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.

Truth be told, this is a pretty big disadvantage, which is why you should use CRTP 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 in 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 T>
struct Base
{
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // or:
        // static_cast<Derived const&>(*this).processB();   // if your prefer by reference
        // ...
    }
};

struct Derived1
    : public Base<Derived1>
{
    void implementation() {
        // ...
    }
};

struct Derived2
    : public Base<Derived2>
{
    void implementation() {
        // ...
    }
};

Github

Github repository link.


0 likes