Search code examples
c++inheritancecrtpstatic-polymorphism

Does the derived class method override the base class version of the method in case the CRTP is used?


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?


Solution

  • 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?
    }