The Curiously Recurring Template Pattern (CRTP) is a C++ idiom where a base class takes a derived class as a template parameter. This allows the base class to call methods from the derived class without virtual functions, leading to better performance and compile-time polymorphism.
Table of Contents
🔹 Basic Structure of CRTP
template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
std::cout << "Derived class implementation\n";
}
};
int main() {
Derived d;
d.interface(); // Calls Derived::implementation() via Base
}
✅ No virtual functions → Faster execution
✅ Compile-time polymorphism → No runtime overhead
🔹 Why Use CRTP?
CRTP is useful for:
✅ Static Polymorphism – Avoids virtual function overhead.
✅ Mixin Classes – Adding behavior without multiple inheritance complexity.
✅ Method Overriding – Derived class methods can be called from the base class.
✅ Compile-time Interface Checking – Ensures certain methods exist in the derived class.
🔹 CRTP Use Cases in C++
1️⃣ Compile-Time Polymorphism
Instead of using virtual functions, CRTP allows method overriding at compile-time.
#include <iostream>
template <typename Derived>
class Base {
public:
void greet() {
static_cast<Derived*>(this)->greetImpl();
}
};
class English : public Base<English> {
public:
void greetImpl() {
std::cout << "Hello!\n";
}
};
class Spanish : public Base<Spanish> {
public:
void greetImpl() {
std::cout << "¡Hola!\n";
}
};
int main() {
English e;
Spanish s;
e.greet(); // Hello!
s.greet(); // ¡Hola!
}
✅ Compile-time dispatch instead of runtime polymorphism
✅ No need for virtual functions, making the code more efficient
2️⃣ Mixin Pattern (Adding Functionality)
CRTP allows adding behavior without multiple inheritance issues.
template <typename Derived>
class Logger {
public:
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
class Application : public Logger<Application> {
public:
void run() {
log("Application started!");
}
};
int main() {
Application app;
app.run(); // Output: [LOG] Application started!
}
✅ Lightweight mixins
✅ No virtual table (vtable) overhead
3️⃣ Method Enforcement in Derived Classes
CRTP can enforce that a derived class implements a required method.
template <typename Derived>
class Base {
public:
void execute() {
static_cast<Derived*>(this)->process();
}
};
class ValidDerived : public Base<ValidDerived> {
public:
void process() {
std::cout << "Processing...\n";
}
};
// class InvalidDerived : public Base<InvalidDerived> {}; // ERROR! Missing process()
int main() {
ValidDerived v;
v.execute(); // Output: Processing...
}
✅ Compile-time method enforcement
✅ No need for abstract base classes
4️⃣ Counting Objects (Static Data in CRTP)
CRTP allows each derived class to have its own static members.
template <typename Derived>
class Counter {
public:
static int count;
Counter() { ++count; }
~Counter() { --count; }
static int getCount() { return count; }
};
template <typename Derived>
int Counter<Derived>::count = 0;
class A : public Counter<A> {};
class B : public Counter<B> {};
int main() {
A a1, a2;
B b1;
std::cout << "A count: " << A::getCount() << "\n"; // Output: A count: 2
std::cout << "B count: " << B::getCount() << "\n"; // Output: B count: 1
}
✅ Each derived class gets its own static count
✅ No global static variables
🔹 CRTP vs Virtual Functions
Feature | CRTP | Virtual Functions |
---|---|---|
Polymorphism Type | Compile-time | Runtime |
Performance | Faster (no vtable) | Slower (due to vtable lookup) |
Flexibility | Less flexible | More flexible (supports dynamic dispatch) |
Memory Overhead | None | vtable adds memory overhead |
Use Case | Known types at compile-time | Need runtime polymorphism |
📌 Use CRTP when performance matters and you don’t need runtime polymorphism.
📌 Use virtual functions when you need dynamic behavior.
🔹 Summary
✅ CRTP is a powerful design pattern for compile-time polymorphism.
✅ It removes the need for virtual functions, making the code faster.
✅ It’s useful for mixins, method enforcement, and static polymorphism.
✅ Each derived class can have independent static members.