C++中的CRTP全称为Curiously Recurring Template Pattern,即奇特递归模板模式。它的核心写法是让一个派生类把自己作为模板参数传给基类,从而在编译期建立类型绑定关系,而不是通过虚函数实现运行时多态。

CRTP的基本结构
下面展示一个最简单的CRTP写法,基类是一个模板类,派生类在继承时把自身类型传入:
// 基类模板,接受派生类类型作为模板参数
template <typename Derived>
class Base {
public:
void interface() {
// 通过静态转换调用派生类方法
static_cast<Derived*>(this)->implementation();
}
};
// 派生类将自身作为模板参数传给Base
class Derived : public Base<Derived> {
public:
void implementation() {
// 具体实现逻辑
}
};
CRTP的常见应用
静态多态
使用CRTP可以在不引入虚函数表的情况下实现类似多态的行为。因为函数调用在编译期就已经确定,所以没有运行时开销:
template <typename T>
class Shape {
public:
void draw() {
static_cast<T*>(this)->draw_impl();
}
};
class Circle : public Shape<Circle> {
public:
void draw_impl() {
// 画圆逻辑
}
};
class Square : public Shape<Square> {
public:
void draw_impl() {
// 画方逻辑
}
};
为派生类提供通用能力
基类可以基于派生类类型提供统一的方法,例如计数对象实例数量:
template <typename T>
class Counter {
public:
static int count;
Counter() { ++count; }
~Counter() { --count; }
};
template <typename T>
int Counter<T>::count = 0;
class MyType : public Counter<MyType> {};
// 使用方式
// MyType a, b;
// int n = MyType::count; // n为2
编译期策略注入
通过CRTP可以把某些行为策略在编译期固定下来,例如为不同派生类提供不同的相等比较逻辑:
template <typename Derived>
class Equatable {
public:
bool operator==(const Derived& other) const {
return static_cast<const Derived*>(this)->equals(other);
}
};
class Point : public Equatable<Point> {
public:
int x, y;
bool equals(const Point& other) const {
return x == other.x && y == other.y;
}
};
使用注意事项
CRTP虽然高效,但也存在一些限制:
- 基类无法访问派生类还未定义的成员,容易产生编译顺序问题
- 多层继承时类型关系会变得复杂,可读性下降
- 不支持运行时的动态类型替换,只能用于编译期确定的类型
在真实项目中,标准库中的<iostream>部分组件以及Boost库中的运算符重载工具都大量使用了CRTP思想。掌握这种模式能够让你在模板元编程中更自如地控制类型和行为。