Delegate behavior to derived classes
1234567891011121314151617181920212223242526272829303132333435 | template<typename derived>
class base
{
public:
void do_something()
{
// ...
static_cast<derived*>(this)->do_something_impl();
// ...
}
private:
void do_something_impl()
{
// Default implementation
}
};
class foo : public base<foo>
{
public:
void do_something_impl()
{
// Derived implementation
}
};
class bar : public base<bar>
{ };
template<typename derived>
void use(base<derived>& b)
{
b.do_something();
} |
This pattern is licensed under the CC0 Public Domain Dedication.
Intent
Delegate behavior to derived classes without incurring the cost of run-time polymorphism.
Description
With the Curiously Recurring Template Pattern (CRTP), which
provides a form of static polymorphism, we can delegate behavior
from a base class to its derived classes. This approach avoids the
costs associated with using virtual functions for run-time
polymorphism, typically implemented with a virtual function
table (a
dynamic dispatch mechanism).
Classes foo and bar, on lines 19–29, demonstrate the CRTP idiom by
inheriting from the base class template (lines 1–17) and providing
themselves as the template argument. For example, foo inherits
from base<foo> on line 19. This allows base to know which
class it is being inherited by at compile-time.
The base class provides a public member function,
do_something (lines 5–10), which depends on do_something_impl, an
internal function that may optionally be overriden by derived
classes. In this way, base is able to delegate behavior to
derived classes. A default implementation for this function is
given on lines 13–16, while the class foo provides its own
implementation on lines 22–25. To ensure that the correct
implementation is used, the do_something function casts
this to a pointer to the derived type on line 8 and calls
do_something_impl on it.
The use function template on lines 31–35 takes a reference to any
instantiation of base and calls do_something on it. As the
derived type is known at compile-time, the correct implementation
function is called without the need for dynamic dispatch. If a
base<foo> is provided, for example, foo’s implementation (lines 22–25)
will be invoked. For a base<bar>, on the other hand, the
default implementation defined by base will be used (lines 13–16).