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 .
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.
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<.
}