On this page
article
C++ Template Idioms
CRTP (Curiously Recurring Template Pattern)
개념
- 기반 클래스에서 파생 클래스의 이름을 사용하는 패턴
- 파생 클래스가 자신의 타입을 기반 클래스의 템플릿 인자로 전달
- 가상함수를 사용하지않고 가상함수처럼 동작
예제: Window event 처리
#include <iostream>
class BaseWindow {
public:
void Click() { }
void MouseMove() { }
};
template<typename T> // CRTP
class Window : public BaseWindow { // Thin Template
public:
void event_loop() {
static_cast<T*>(this)->Click(); // 파생 클래스의 Click 호출
}
};
class MainWindow : public Window<MainWindow> {
public:
void Click() { std::cout << "MainWindow Click\n"; }
};
class MainWindow2 : public Window<MainWindow2> {
public:
void Click() { std::cout << "MainWindow2 Click\n"; }
};
int main() {
MainWindow w;
w.event_loop(); // MainWindow Click
}
CRTP 예제 2: 객체 수 제한
문제 상황
- 각 클래스마다 생성할 수 있는 객체의 최대 개수를 제한하고 싶다.
#include <iostream>
#include <exception>
class too_many_object : std::exception {};
template<typename T, int MAXCOUNT>
class LimitObjectCount {
inline static int maxcnt = 0;
public:
LimitObjectCount() {
if (++maxcnt > MAXCOUNT)
throw too_many_object();
}
~LimitObjectCount() { --maxcnt; }
};
class Player : public LimitObjectCount<Player, 5> {};
class Judge : public LimitObjectCount<Judge, 3> {};
int main() {
Player p[3]; // ok
Judge j[5]; // runtime 예외 발생
}
SFINAE (Substitution Failure Is Not An Error)
개념
- 함수 오버로딩 해석 시 타입 대입 실패는 에러가 아니라 다른 후보 탐색으로 이어짐
함수 찾는 순서:
- exact match
- template
- promotion
- standard conversion
- variable argument
※ ref, namespace, ADL 등이 섞이면 더 복잡
enable_if
개념
- 특정 타입에 대해서만 템플릿 함수/클래스 사용 가능하도록 제한
- C++20~:
requires
사용 - ~C++17:
enable_if
사용
예제
#include <print>
#include <type_traits>
void foo(...) { std::println("..."); }
template<typename T>
std::enable_if_t<std::is_integral_v<T>>
foo(T a) {
std::println("T");
}
int main() {
foo(3); // T
foo(3.4); // ...
}
enable_if 생성자 예제
#include <type_traits>
class Animal {};
class Dog : public Animal {};
template<typename T>
class smart_ptr {
T* obj;
public:
smart_ptr(T* p = nullptr) : obj{p} {}
template<typename U, typename X = std::enable_if_t<std::is_convertible_v<U*, T*>>>
smart_ptr(const smart_ptr<U>& other) : obj{other.obj} {}
template<typename> friend class smart_ptr;
};
int main() {
smart_ptr<Dog> sp1{new Dog};
smart_ptr<Animal> sp2 = sp1; // OK (Dog* → Animal* 변환 가능)
smart_ptr<int> sp3 = sp1; // 컴파일 에러
}
요약
- CRTP: 파생 클래스 타입을 기반 클래스의 템플릿 인자로 전달
- enable_if: 특정 조건 만족할 때만 템플릿 인스턴스 허용
- SFINAE: 대입 실패는 에러가 아니고 다른 후보 탐색으로 넘어감