Curiously Recurring Template Pattern (CRTP)
Introduced after showcasing Visitor Pattern, and how there was much code, because there is going to be a lot of methods. Learned in CS247 - Software Engineering Principles.
CRTP is a C++ programming pattern that involves creating a template class with a derived class as its template parameter. This allows the template class to know the type of the derived class and use it as if it were a base class.
Template our superclass with a type parameter - inherit AND substitute the derived class type.
Learned in CS247 - Software Engineering Principles.
Why CRTP exists?
We can’t copy an abstract class, thus why we use CRTP.
For example go to Lecture 21 in CS247 Lectures. But end-result:
class Enemy {
public:
virtual void beStruckBy(Weapon& w) = 0;
virtual ~Enemy() {}
};
template<typename T> class EnemyBeStruck: public Enemy {
public:
void beStruckBy(Weapon& w) override {
w.strike(*static_cast<T*>(this));
}
virtual ~EnemyBeStruck() = 0;
}
template<typename T> EnemyBeStruck<T>::~EnemyBeStruck<T>() {}
class Turtle: public EnemyBeStruck<Turtle> {...}
class Monster: public EnemyBeStruck<Monster> {...}
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.
Weapon* w = ...;
Enemy* e = new Turtle{...} / new Monster{...};
e->beSstruckBy(*w);
Another problem CRTP can solve: polymorphic cloning. Recall abstract books.
No relations between Template Method Pattern and CRTP
The two are unrelated. Template method pattern uses the word template as in English: the draw algorithm for turtles provides a “template” as to what all invocations of draw should look like.
CRTP on the other hand, is referring to C++ templates, i.e classes that can be parameterized on a type.
AbstractBook example:
class AbstractBook{
public:
virtual AbstrackBook* clone = 0;
virtual ~AbstrackBook(){}
};
template<typename T> class BookClonable: public AbstrackBook {
public:
T* clone() override {
return T{*static_cast<T*>(this)}
}
virtual ~BookClonable() = 0;
};
template<typnename T> BookClonable<T>::BookClonable<T>(){}
class Text: public BookClonable<Text>{...}
class Comic: public BookClonable<Comic>{...}
AbstrackBook* b = new Text{...} / new Comic{...};
AbstrackBook* b2 = b->clone():
w
Example:
// A class that defines `operator<` can define the other methods in terms of the < operator. For example: (a > b) is equivalent to (b < a). (a == b) is equivalent to !(a < b) && !(b < a). Write a CRTP class Compareable. Compareable should add the various comparison operators to a class that supports operator<. Give an example of a class that implements operator< and show how Compareable can be used with it. Explain why one might prefer this over an abstract class "AbstractCompareable" that defines the various comparison operators in terms of <, and defines operator< as a virtual method to be overridden in the subclass.
#include <iostream>
using namespace std;
template <typename T> class Comparable {
public:
friend bool operator>(const T& lhs, const T& rhs) {
return rhs < lhs;
}
friend bool operator>=(const T& lhs, const T& rhs) {
return !(lhs < rhs);
}
friend bool operator<=(const T& lhs, const T& rhs) {
return !(lhs > rhs);
}
friend bool operator==(const T& lhs, const T& rhs) {
return !(lhs < rhs) && !(lhs > rhs);
}
friend bool operator!=(const T& lhs, const T& rhs) {
return !(lhs == rhs);
}
};
// example of how to use the Comparable CRTP class with a class that implements operator<
class RandomName : public Comparable<RandomName> {
int x;
public:
RandomName(int x) : x(x) {}
friend bool operator<(const RandomName& lhs, const RandomName& rhs) {
return lhs.x < rhs.x;
}
};
int main() {
RandomName a(1);
RandomName b(2);
cout << (a < b) << endl;
cout << (a > b) << endl;
cout << (a <= b) << endl;
cout << (a >= b) << endl;
cout << (a == b) << endl;
cout << (a != b) << endl;
RandomName c(5);
RandomName d(10);
if (c < d) std::cout << "a is less than b" << std::endl;
if (c > d) std::cout << "a is greater than b" << std::endl;
if (c == d) std::cout << "a is equal to b" << std::endl;
return 0;
}
// Explain why one might prefer this over an abstract class "AbstractCompareable" that defines the various comparison operators in terms of <, and defines operator< as a virtual method to be overridden in the subclass.
// The CRTP class is more efficient than the abstract class because the abstract class requires a virtual function call, which is slower than a normal function call. The CRTP class is also more flexible because it allows the subclass to define operator< in terms of other operators, such as operator==. The abstract class does not allow this flexibility because it requires operator< to be defined in terms of operator<.
}