[2026] C++ CRTP Pattern Complete Guide | Static Polymorphism & Compile-Time Optimization
이 글의 핵심
Master C++ CRTP for static polymorphism without virtual function overhead. Complete guide with mixin counters, comparable helpers, singleton patterns, and performance comparisons.
What is CRTP?
Compile-time polymorphism and policy design connect with Strategy and PIMPL discussions in the comprehensive pattern guide, and as FAQ mentions, creation points are often considered together with Factory. Curiously Recurring Template Pattern
- Pass derived class as template argument to base class
- Static polymorphism (compile time)
- Implement polymorphism without virtual functions 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Base class
template<typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
// Derived class
class Derived : public Base<Derived> {
public:
void implementation() {
cout << "Derived implementation" << endl;
}
};
int main() {
Derived d;
d.interface(); // "Derived implementation"
}
Virtual Functions vs CRTP
Virtual Functions (Dynamic Polymorphism)
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Base {
public:
virtual void func() = 0;
virtual ~Base() {}
};
class Derived : public Base {
public:
void func() override {
cout << "Derived" << endl;
}
};
// Runtime overhead (vtable)
CRTP (Static Polymorphism)
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename Derived>
class Base {
public:
void func() {
static_cast<Derived*>(this)->funcImpl();
}
};
class Derived : public Base<Derived> {
public:
void funcImpl() {
cout << "Derived" << endl;
}
};
// Compile time, no overhead
Practical Examples
Example 1: Counter Mixin
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename Derived>
class Countable {
private:
static int count;
public:
Countable() { count++; }
Countable(const Countable&) { count++; }
~Countable() { count--; }
static int getCount() { return count; }
};
template<typename Derived>
int Countable<Derived>::count = 0;
class Widget : public Countable<Widget> {
public:
Widget() { cout << "Widget created" << endl; }
};
class Gadget : public Countable<Gadget> {
public:
Gadget() { cout << "Gadget created" << endl; }
};
int main() {
Widget w1, w2;
Gadget g1;
cout << "Widget count: " << Widget::getCount() << endl; // 2
cout << "Gadget count: " << Gadget::getCount() << endl; // 1
}
Example 2: Auto-Generate Comparison Operators
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename Derived>
class Comparable {
public:
friend bool operator!=(const Derived& lhs, const Derived& rhs) {
return !(lhs == rhs);
}
friend bool operator>(const Derived& lhs, const Derived& rhs) {
return rhs < lhs;
}
friend bool operator<=(const Derived& lhs, const Derived& rhs) {
return !(rhs < lhs);
}
friend bool operator>=(const Derived& lhs, const Derived& rhs) {
return !(lhs < rhs);
}
};
class Point : public Comparable<Point> {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
// Only implement == and <, rest auto-generated
friend bool operator==(const Point& lhs, const Point& rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y;
}
friend bool operator<(const Point& lhs, const Point& rhs) {
if (lhs.x != rhs.x) return lhs.x < rhs.x;
return lhs.y < rhs.y;
}
};
int main() {
Point p1(1, 2);
Point p2(3, 4);
cout << (p1 == p2) << endl; // 0
cout << (p1 != p2) << endl; // 1
cout << (p1 < p2) << endl; // 1
cout << (p1 >= p2) << endl; // 0
}
Example 3: Singleton Mixin
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename Derived>
class Singleton {
protected:
Singleton() {}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Derived& getInstance() {
static Derived instance;
return instance;
}
};
class Config : public Singleton<Config> {
friend class Singleton<Config>;
private:
Config() { cout << "Config created" << endl; }
int value = 0;
public:
void setValue(int v) { value = v; }
int getValue() { return value; }
};
class Logger : public Singleton<Logger> {
friend class Singleton<Logger>;
private:
Logger() { cout << "Logger created" << endl; }
public:
void log(const string& msg) {
cout << "[LOG] " << msg << endl;
}
};
int main() {
Config::getInstance().setValue(100);
Logger::getInstance().log("System started");
cout << Config::getInstance().getValue() << endl; // 100
}
Example 4: Chaining Interface
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename Derived>
class Chainable {
protected:
Derived& self() {
return static_cast<Derived&>(*this);
}
};
class QueryBuilder : public Chainable<QueryBuilder> {
private:
string query;
public:
QueryBuilder& select(const string& fields) {
query = "SELECT " + fields;
return self();
}
QueryBuilder& from(const string& table) {
query += " FROM " + table;
return self();
}
QueryBuilder& where(const string& condition) {
query += " WHERE " + condition;
return self();
}
string build() {
return query;
}
};
int main() {
QueryBuilder qb;
string sql = qb.select("*")
.from("users")
.where("age > 18")
.build();
cout << sql << endl;
// SELECT * FROM users WHERE age > 18
}
Performance Comparison
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <chrono>
// Virtual functions
class VirtualBase {
public:
virtual int compute(int x) = 0;
virtual ~VirtualBase() {}
};
class VirtualDerived : public VirtualBase {
public:
int compute(int x) override {
return x * 2;
}
};
// CRTP
template<typename Derived>
class CRTPBase {
public:
int compute(int x) {
return static_cast<Derived*>(this)->computeImpl(x);
}
};
class CRTPDerived : public CRTPBase<CRTPDerived> {
public:
int computeImpl(int x) {
return x * 2;
}
};
int main() {
const int N = 100000000;
// Virtual functions
VirtualBase* vb = new VirtualDerived();
auto start = chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
vb->compute(i);
}
auto end = chrono::high_resolution_clock::now();
cout << "Virtual: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;
// CRTP
CRTPDerived cd;
start = chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
cd.compute(i);
}
end = chrono::high_resolution_clock::now();
cout << "CRTP: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;
}
Typical results: CRTP is 2-5x faster than virtual functions in tight loops due to inlining and no vtable lookup.
Common Issues
Issue 1: Wrong casting
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Dangerous
template<typename Derived>
class Base {
public:
void func() {
static_cast<Derived*>(this)->impl();
}
};
class Wrong : public Base<OtherClass> { // Wrong type!
public:
void impl() {}
};
// ✅ Correct usage
class Correct : public Base<Correct> {
public:
void impl() {}
};
Issue 2: Circular dependency
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Circular dependency
class A : public Base<B> {}; // B not yet defined
class B : public Base<A> {};
// ✅ Each uses itself as template argument
class A : public Base<A> {};
class B : public Base<B> {};
Issue 3: Missing virtual destructor
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Possible memory leak
template<typename Derived>
class Base {
// No virtual destructor
};
// ✅ Add virtual destructor (for polymorphic deletion)
template<typename Derived>
class Base {
public:
virtual ~Base() = default;
};
CRTP Use Scenarios
1. Performance-critical cases
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Game engines, high-performance computing
template<typename Derived>
class Entity {
public:
void update(float dt) {
static_cast<Derived*>(this)->updateImpl(dt);
}
};
2. Code reuse
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Common functionality as mixin
template<typename Derived>
class Serializable {
public:
string serialize() {
// Serialization logic
}
};
3. Compile-time polymorphism
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Polymorphism via template argument
template<typename T>
void process(T& obj) {
obj.compute(); // Decided at compile time
}
FAQ
Q1: When to use CRTP?
A:
- Performance-critical cases
- Compile-time polymorphism needed
- Mixin patterns
Q2: Always CRTP instead of virtual functions?
A: No. Use virtual functions when runtime polymorphism needed.
Q3: CRTP disadvantages?
A:
- Increased code complexity
- Longer compile times
- No runtime polymorphism
Q4: What are mixins?
A: Pattern combining multiple base classes to add functionality.
Q5: CRTP vs Template Method pattern?
A: CRTP is static, Template Method is dynamic.
Q6: CRTP learning resources?
A:
- “Modern C++ Design” (Andrei Alexandrescu)
- “C++ Templates: The Complete Guide”
- Boost library source code
Related Articles
- C++ Policy-Based Design Complete Guide
- C++ CRTP Complete Guide | Static Polymorphism & Compile-Time Optimization
- C++ Template Template Parameters Guide
Related Posts
- C++ CRTP Complete Guide | Static Polymorphism & Compile-Time Optimization
- C++ Policy-Based Design
- C++ auto Type Deduction | Let Compiler Handle Complex Types
- C++ CTAD
- C++20 Concepts Complete Guide | New Era of Template Constraints
Keywords
C++, CRTP, static polymorphism, templates, mixins, compile-time optimization, design patterns