Jump to content

More C++ Idioms/Curiously Recurring Template Pattern

From Wikibooks, open books for an open world

Curiously Recurring Template Pattern

[edit | edit source]

Intent

[edit | edit source]

Specialize a base class using the derived class as a template argument.

Also Known As

[edit | edit source]
  • CRTP
  • Mixin-from-above
  • Static polymorphism
  • Simulated dynamic binding
  • Upside-down Inheritance

Motivation

[edit | edit source]

To extract out a type-independent but type-customizable functionality in a base class and to mix-in that interface/property/behavior into a derived class, customized for the derived class.

Solution and Sample Code

[edit | edit source]

In CRTP idiom, a class T inherits from a template that specializes on T.

class T : public X<T> {};

This is valid only if the size of X<T> can be determined independently of T. Typically, the base class template will take advantage of the fact that member function bodies (definitions) are not instantiated until long after their declarations, and will use members of the derived class within its own member functions, via the use of a static_cast, e.g.:

  template <class Derived>
  struct base
  {
      void interface()
      {
          // ...
          static_cast<Derived*>(this)->implementation();
          // ...
      }
  
      static void static_interface()
      {
          // ...
          Derived::static_implementation();
          // ...
      }

      // The default implementation may be (if exists) or should be (otherwise) 
      // overridden by inheriting in derived classes (see below)
      void implementation();
      static void static_implementation();
  };

  // The Curiously Recurring Template Pattern (CRTP)
  struct derived_1 : base<derived_1>
  {
      // This class uses base variant of implementation
      //void implementation();
      
      // ... and overrides static_implementation
      static void static_implementation();
  };

  struct derived_2 : base<derived_2>
  {
      // This class overrides implementation
      void implementation();

      // ... and uses base variant of static_implementation
      //static void static_implementation();
  };

C++23

[edit | edit source]

C++23 added a new feature called explicit object parameter, which allows you to pass the object a method is called on as the first parameter of a method instead of *this. When calling the method, as with all function calls, upcasts are only performed if necessary, making it possible to change the behavior of a method for derived classes without overriding it by using a method template or auto. If the CRTP base class you would have written only has non-static methods, this removes the need to pass T as a template parameter, making it similar to deriving from an ordinary base class.

class T : public X {}; // note: X instead of X<T>

One benefit of this approach is that if T is a base class of Derived, the mixin methods of Derived will use Derived methods rather than T methods. Another is that it makes const and non-const method overloads unnecessary because the explicit object parameter can bind to const and non-const references if a constraint is not used to prevent this.

#include <type_traits>
#include <utility>

struct base
{
    // When calling `interface` on a derived class, a reference to derived class
    // is passed in as `val` instead of upcasting to `base&` or `base&&`
    void interface(this auto&& val)
    // Constraint prevents const call, `auto&&` explicit object parameter could
    // bind to const references if this method was unconstrained
    requires (!std::is_const_v<std::remove_reference_t<decltype(val)>>)
    {
        // ...
        // Calls rvalue overload if val is a temporary
        std::forward<decltype(val)>(val).implementation();
        // ...
    }
};

// Note that `base` is derived from instead of `base<derived_1>`
struct derived_1 : base
{
    void implementation();
};

struct derived_2 : derived_1
{
    // Calling `derived_2::interface` will call `derived_2::implementation`
    // because `derived_1` was not passed into `base`, whereas if `base` was a
    // traditional CRTP class template `derived_1` would inherit from
    // `base<derived_1>` and `derived_2::interface` would
    // `static_cast<derived_1*>` and call `derived_1::implementation`
    void implementation();
};

Known Uses

[edit | edit source]

Barton-Nackman trick

[edit | edit source]

References

[edit | edit source]

Curiously Recurring Template Pattern on Wikipedia