Curiously Recurring Template Pattern (CRTP)
Learned in CS247.
When to use this pattern?
There are 2 use cases:
- Reduce boilerplate code while maintaining compile-time polymorphism
- Polymorphic cloning
So it seems that with CRTP, you always have 3 layers in the hierarchy, due to the nature of the templates, which means that they are actually different superclasses, so you have to have these superclasses inherit from a higher superclass that doesn’t use templates.
Motivation
Introduced after showcasing Visitor Pattern, and how there was much code, because there is going to be a lot of methods: if we have subclasses of and subclasses of : different methods to write.
Annoying part: Boilerplate - write the following in each DerivedEnemy
:
- must be one for every single
DerivedEnemy
- Cannot put it in
Enemy
:
However, notice that this doesn’t work - the type of this
is wrong. It’s not telling us what the dynamic type is.
Solution to fixing the boilerplate code is to use Curiously Recurring Template Pattern (CRTP).
Example 1
Template our superclass with a type parameter - inherit, AND substitute the derived class type.
How do we use this to fix the boilerplate code?
This sort of works
Cast this
from type Enemy<Turtle>*
to Turtle*
allows us to override into Rock::Strike(Turtle&)
and Stick::Strike(Turtle&)
.
Issue: Now, we have different superclasses for each Enemy
Because Enemy<Turtle>
and Enemy<Monster>
are different classes: can no longer use Enemy
s polymorphically - no vector<Enemy*>
allowed!
Solution: Add another layer of inheritance.
Now, we have a public interface by which all our concrete enemies follow: can all beStruckBy
weapons. We use the virtual method in Enemy
to resolve beStruckBy
to either EnemyBeStruck<Turtle>
or EnemyByStruck<Monster>
. Then just static_cast
to T*
- and we’re good.
Example #2
Another problem CRTP can solve: polymorphic cloning.
Recall abstract books:
Say I have:
I want a deep copy of whatever b points to. I cannot just do this:
This attempts to create an AbstractBook
by invoking its constructor. Wrong for 2 reasons:
AbstractBook
is abstract, cannot instantiate those objects- Ignoring what we’re actually pointing at, we actually want to invoke a constructor that depends on the dynamic type of
b
We can provide a virtual clone
method for the purpose of solving this:
Instead use the copy ctor in each of our clone
methods to simplify the implementation.
Exact same code in MermaidBook
and Comic
- just the type of this
and the type of ctor which is changing. Once again, we can use CRTP.
b->clone
is virtual, so if b
points at a Comic
, we call BookClonable<Comic>::clone
- static_cast
this
into a Comic*
and invoke the Comic
copy constructor with the Comic&
.
Provided for all subclasses - reduces boilerplate code.