Curiously recurring template pattern
The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class HistoryThe technique was formalized in 1989 as "F-bounded quantification."[2] The name "CRTP" was independently coined by Jim Coplien in 1995,[3] who had observed it in some of the earliest C++ template code as well as in code examples that Timothy Budd created in his multiparadigm language Leda.[4] It is sometimes called "Upside-Down Inheritance"[5][6] due to the way it allows class hierarchies to be extended by substituting different base classes. The Microsoft Implementation of CRTP in Active Template Library (ATL) was independently discovered, also in 1995, by Jan Falkin, who accidentally derived a base class from a derived class. Christian Beaumont first saw Jan's code and initially thought it could not possibly compile in the Microsoft compiler available at the time. Following the revelation that it did indeed work, Christian based the entire ATL and Windows Template Library (WTL) design on this mistake.[citation needed] General form// The Curiously Recurring Template Pattern (CRTP)
template <class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
Some use cases for this pattern are static polymorphism and other metaprogramming techniques such as those described by Andrei Alexandrescu in Modern C++ Design.[7]
It also figures prominently in the C++ implementation of the Data, Context, and Interaction paradigm.[8]
In addition, CRTP is used by the C++ standard library to implement the Static polymorphismTypically, 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 cast; e.g.: template <class T>
struct Base
{
void interface()
{
// ...
static_cast<T*>(this)->implementation();
// ...
}
static void static_func()
{
// ...
T::static_sub_func();
// ...
}
};
struct Derived : public Base<Derived>
{
void implementation();
static void static_sub_func();
};
In the above example, the function This technique achieves a similar effect to the use of virtual functions, without the costs (and some flexibility) of dynamic polymorphism. This particular use of the CRTP has been called "simulated dynamic binding" by some.[10] This pattern is used extensively in the Windows ATL and WTL libraries. To elaborate on the above example, consider a base class with no virtual functions. Whenever the base class calls another member function, it will always call its own base class functions. When we derive a class from this base class, we inherit all the member variables and member functions that were not overridden (no constructors or destructors). If the derived class calls an inherited function which then calls another member function, then that function will never call any derived or overridden member functions in the derived class. However, if base class member functions use CRTP for all member function calls, the overridden functions in the derived class will be selected at compile time. This effectively emulates the virtual function call system at compile time without the costs in size or function call overhead (VTBL structures, and method lookups, multiple-inheritance VTBL machinery) at the disadvantage of not being able to make this choice at runtime. Object counterThe main purpose of an object counter is retrieving statistics of object creation and destruction for a given class.[11] This can be easily solved using CRTP: template <typename T>
struct counter
{
static inline int objects_created = 0;
static inline int objects_alive = 0;
counter()
{
++objects_created;
++objects_alive;
}
counter(const counter&)
{
++objects_created;
++objects_alive;
}
protected:
~counter() // objects should never be removed through pointers of this type
{
--objects_alive;
}
};
class X : counter<X>
{
// ...
};
class Y : counter<Y>
{
// ...
};
Each time an object of class Polymorphic chainingMethod chaining, also known as named parameter idiom, is a common syntax for invoking multiple method calls in object-oriented programming languages. Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results. When the named parameter object pattern is applied to an object hierarchy, things can go wrong. Suppose we have such a base class: class Printer
{
public:
Printer(ostream& pstream) : m_stream(pstream) {}
template <typename T>
Printer& print(T&& t) { m_stream << t; return *this; }
template <typename T>
Printer& println(T&& t) { m_stream << t << endl; return *this; }
private:
ostream& m_stream;
};
Prints can be easily chained: Printer(myStream).println("hello").println(500);
However, if we define the following derived class: class CoutPrinter : public Printer
{
public:
CoutPrinter() : Printer(cout) {}
CoutPrinter& SetConsoleColor(Color c)
{
// ...
return *this;
}
};
we "lose" the concrete class as soon as we invoke a function of the base: // v----- we have a 'Printer' here, not a 'CoutPrinter'
CoutPrinter().print("Hello ").SetConsoleColor(Color.red).println("Printer!"); // compile error
This happens because 'print' is a function of the base – 'Printer' – and then it returns a 'Printer' instance. The CRTP can be used to avoid such problem and to implement "Polymorphic chaining":[12] // Base class
template <typename ConcretePrinter>
class Printer
{
public:
Printer(ostream& pstream) : m_stream(pstream) {}
template <typename T>
ConcretePrinter& print(T&& t)
{
m_stream << t;
return static_cast<ConcretePrinter&>(*this);
}
template <typename T>
ConcretePrinter& println(T&& t)
{
m_stream << t << endl;
return static_cast<ConcretePrinter&>(*this);
}
private:
ostream& m_stream;
};
// Derived class
class CoutPrinter : public Printer<CoutPrinter>
{
public:
CoutPrinter() : Printer(cout) {}
CoutPrinter& SetConsoleColor(Color c)
{
// ...
return *this;
}
};
// usage
CoutPrinter().print("Hello ").SetConsoleColor(Color.red).println("Printer!");
Polymorphic copy constructionWhen using polymorphism, one sometimes needs to create copies of objects by the base class pointer. A commonly used idiom for this is adding a virtual clone function that is defined in every derived class. The CRTP can be used to avoid having to duplicate that function or other similar functions in every derived class. // Base class has a pure virtual function for cloning
class AbstractShape {
public:
virtual ~AbstractShape () = default;
virtual std::unique_ptr<AbstractShape> clone() const = 0;
};
// This CRTP class implements clone() for Derived
template <typename Derived>
class Shape : public AbstractShape {
public:
std::unique_ptr<AbstractShape> clone() const override {
return std::make_unique<Derived>(static_cast<Derived const&>(*this));
}
protected:
// We make clear Shape class needs to be inherited
Shape() = default;
Shape(const Shape&) = default;
Shape(Shape&&) = default;
};
// Every derived class inherits from CRTP class instead of abstract class
class Square : public Shape<Square>{};
class Circle : public Shape<Circle>{};
This allows obtaining copies of squares, circles or any other shapes by PitfallsOne issue with static polymorphism is that without using a general base class like Deducing thisThe use of CRTP can be simplified using the C++23 feature deducing this.[13][14] For the function template <typename T>
struct ChefBase
{
void signature_dish()
{
static_cast<T*>(this)->cook_signature_dish();
}
};
struct CafeChef : ChefBase<CafeChef>
{
void cook_signature_dish() {}
};
If explicit object parameter is used, struct ChefBase
{
template <typename Self>
void signature_dish(this Self&& self)
{
self.cook_signature_dish();
}
};
struct CafeChef : ChefBase
{
void cook_signature_dish() {}
};
See alsoReferences
|