A couple days ago I found an interesting C++ construct named Curiously Recurring Template Pattern (usually known under abbreviation CRTP). From that time I have been attempting to fully understand how does this trick work.
Let's say I have following code snippet
#include <iostream>
template<class T>
struct GraphicalObject
{
void draw()
{
static_cast<T*>(this)->draw();
}
};
struct Circle : GraphicalObject<Circle>
{
void draw()
{
std::cout << "Drawing circle!" << std::endl;
}
};
struct Square : GraphicalObject<Square>
{
void draw()
{
std::cout << "Drawing square!" << std::endl;
}
};
int main(int argc, char** argv) {
Square s;
Circle c;
s.draw();
c.draw();
return 0;
}
All the "magic" behind the CRTP is obviously in the line static_cast<T*>(this)->draw()
. But how does it work in detail? My understanding was following. Let's say I have the Circle c
.
In case the compiler see this declaration it is a signal for him to instantiate the GraphicalObject
template with argument Circle
. I think that this results in creation of the struct GraphicalObject<Circle>
by the compiler
struct GraphicalObject<Circle>
{
void draw()
{
static_cast<Circle*>(this)->draw();
}
};
So struct with a special version of the draw
method is created. The struct GraphicalObject<Circle>
forms the base struct for the Circle
struct.
In the second step the instance c
of the Circle
struct is created i.e. the implicit constructor of the Circle
is invoked. Due to the fact that the Circle
is inherited from the GraphicalObject<Circle>
the implicit constructor of that struct is invoked at first. This results in creation of an instance of the GraphicalObject<Circle>
inside the c
object in my opinion. Is it right? Then I think that the draw
method declared in the Circle
struct overrides the draw
method declared in the GraphicalObject<Circle>
struct. But this doesn't make sense for me. I would rather expect that the "specialized" version of the draw
method comming from the GraphicalObject<Circle>
shall not be overridden. Please can you tell me what is wrong in my understanding how the CRTP works?
You're not doing anything polymorphically in main
which would demonstrate the features of CRTP. Try this instead and see what happens:
template <typename T>
void drawObject(GraphicalObject<T>& obj) {
obj.draw();
}
int main() {
Square s;
Circle c;
s.draw();
c.draw();
drawObject(s); // what happens here?
GraphicalObject<Circle>& c_ref = c;
drawObject(c_ref); // what happens here?
}