C/C++ Arena

Step 8 of 9

CRTP and static polymorphism

Virtual functions choose which code to run at run time, through a hidden pointer to a table of functions. That's flexible, but every call is an indirect jump the compiler usually can't inline. When the concrete type is known at compile time anyway, the Curiously Recurring Template Pattern (CRTP) gets the same "the base class calls the derived class's version" effect with ordinary, inlinable calls. The name comes from its odd shape: a class inherits from a template instantiated with itself.

#include <iostream>
#include <string>

template <typename Derived>
struct Greeter {
    void greet() const {
        // The base knows its real type at compile time, so no virtual call is needed.
        const auto& self = static_cast<const Derived&>(*this);
        std::cout << "Hello from " << self.name() << "\n";
    }
};

struct London : Greeter<London> {
    std::string name() const { return "London"; }
};

struct Madrid : Greeter<Madrid> {
    std::string name() const { return "Madrid"; }
};

template <typename T>
void welcome(const Greeter<T>& g) { g.greet(); }

int main() {
    welcome(London{});
    welcome(Madrid{});
}
Hello from London
Hello from Madrid

How it works

What CRTP is used for

C++23 added "deducing this" (void greet(this const auto& self)), which covers many of these uses without the curious inheritance. You'll still see CRTP everywhere in existing code.

Your turn: write a CRTP base Counted<T> that tracks how many objects of each derived type are alive. Its constructor and copy constructor add one to a counter, its destructor subtracts one, and static int alive() returns the count. Because the counter lives in Counted<Enemy> and Counted<Bullet> separately, each type gets its own count.

Previous: SFINAE and enable_if Next: Challenge: compile-time computation