language_crtp

language_crtp (이상하게 반복되는 템플릿 패턴, CRTP)

Curiously Recurring Template Pattern은 클래스 X가 템플릿 매개변수 Z를 받는 클래스 템플릿 Y로부터, Z = X로 인스턴스화하여 파생되는 관용구예요. 컴파일 타임 다형성을 구현하는 대표적인 기법이에요.

출처: cppreference

본문

예를 들어 다음과 같이, 클래스 X가 자기 자신을 템플릿 인자로 넘긴 Y<X>로부터 상속받아요.

template<class Z>
class Y {};

class X : public Y<X> {};

예제

CRTP는 "컴파일 타임 다형성"을 구현하는 데 쓸 수 있어요. 기반 클래스가 인터페이스를 노출하고, 파생 클래스가 그 인터페이스를 구현하는 방식이에요.

#include <cstdio>

#ifndef __cpp_explicit_this_parameter // 전통적인 문법

template <class Derived>
struct Base
{
    void name() { static_cast<Derived*>(this)->impl(); }
protected:
    Base() = default; // Base 객체 생성 방지 (UB)
};
struct D1 : public Base<D1> { void impl() { std::puts("D1::impl()"); } };
struct D2 : public Base<D2> { void impl() { std::puts("D2::impl()"); } };

#else // C++23 deducing-this 문법

struct Base { void name(this auto&& self) { self.impl(); } };
struct D1 : public Base { void impl() { std::puts("D1::impl()"); } };
struct D2 : public Base { void impl() { std::puts("D2::impl()"); } };

#endif

int main()
{
    D1 d1; d1.name();
    D2 d2; d2.name();
}

출력:

D1::impl()
D2::impl()

전통적인 CRTP에서는 기반 클래스가 static_cast<Derived*>(this)->impl()처럼 자기 타입을 Derived로 캐스팅해 파생 클래스의 구현을 호출해요. C++23부터는 deducing this 문법으로 더 간결하게 쓸 수 있어요. 이렇게 하면 가상 함수 호출 없이 컴파일 타임에 바인딩되는 다형적 동작을 만들 수 있어요.

더 알아보기 (Learn more)

cppreference