C++ 开发必须了解的 CRTP(奇异递归模板模式)
- 一、详解
-
- 1、 什么是 CRTP?
- 2、 CRTP 的核心原理
-
- 2.1、 静态多态的实现
- 2.2 、关键特性
- 3、CRTP 的典型应用场景
-
- 3.1、 静态接口实现
- 3.2、 对象计数
- 3.3 、链式调用
- 4、CRTP 与虚函数的对比
- 5、 CRTP 的最佳实践
-
- 5.1 、使用 `static_cast` 而非 `dynamic_cast`
- 5.2、 提供保护性析构函数
- 5.3、 使用 CRTP 辅助宏(可选)
- 6、 CRTP 的局限性
- 7、 实际项目中的 CRTP 示例
-
- 7.1 、访问者模式实现
- 7.2、 序列化框架
- 8、总结
- 二、代码示例
-
- 1、示例代码
- 2、运行结果

一、详解
1、 什么是 CRTP?
CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)是 C++ 模板元编程中的一种高级设计模式。它的核心思想是:一个类模板的派生类,将自己作为基类模板的类型参数。
这种模式的名字来源于其奇特的语法形式:
template <typename Derived>
class Base {
// 基类实现
};
class Derived : public Base<Derived> { // 注意这里:Derived 作为模板参数
// 派生类实现
};
从语法上看,Derived 继承自 Base<Derived>,这形成了一个"递归"的继承关系。但实际上,这里的"递归"是编译时的,不会导致无限循环。
2、 CRTP 的核心原理
2.1、 静态多态的实现
CRTP 的核心优势在于实现静态多态(编译时多态),这与传统的虚函数动态多态形成对比:
// 传统虚函数方式(动态多态)
class Animal {
public:
virtual void speak() = 0;
};
class Dog : public Animal {
public:
void speak() override { cout << "Woof!" << endl; }
};
// CRTP 方式(静态多态)
template <typename Derived>
class AnimalBase {
public:
void speak() {
static_cast<Derived*>(this)->speakImpl();
}
};
class Dog : public AnimalBase<Dog> {
public:
void speakImpl() { cout << "Woof!" << endl; }
};
2.2 、关键特性
3、CRTP 的典型应用场景
3.1、 静态接口实现
template <typename Derived>
class Comparable {
public:
bool operator==(const Derived& other) const {
return !(static_cast<const Derived&>(*this) < other) &&
!(other < static_cast<const Derived&>(*this));
}
bool operator!=(const Derived& other) const {
return !(*this == other);
}
};
class MyInt : public Comparable<MyInt> {
public:
int value;
MyInt(int v) : value(v) {}
bool operator<(const MyInt& other) const {
return value < other.value;
}
};
3.2、 对象计数
template <typename T>
class Counter {
public:
static int getCount() { return count; }
protected:
Counter() { ++count; }
Counter(const Counter&) { ++count; }
~Counter() { —count; }
private:
static int count;
};
template <typename T>
int Counter<T>::count = 0;
class MyObject : public Counter<MyObject> {
// 自动获得计数功能
};
3.3 、链式调用
template <typename Derived>
class Builder {
public:
Derived& setX(int x) {
// 设置逻辑
return static_cast<Derived&>(*this);
}
Derived& setY(int y) {
// 设置逻辑
return static_cast<Derived&>(*this);
}
};
class MyBuilder : public Builder<MyBuilder> {
public:
MyBuilder& build() {
// 构建逻辑
return *this;
}
};
// 使用:支持链式调用
MyBuilder().setX(10).setY(20).build();
4、CRTP 与虚函数的对比
| 性能 | 零运行时开销,可内联 | 有虚函数表开销 |
| 编译时间 | 编译时实例化,可能增加编译时间 | 编译时间较短 |
| 二进制大小 | 可能增加(模板实例化) | 相对较小 |
| 灵活性 | 编译时确定,不够灵活 | 运行时多态,更灵活 |
| 类型安全 | 编译时检查,更安全 | 运行时可能出错 |
5、 CRTP 的最佳实践
5.1 、使用 static_cast 而非 dynamic_cast
template <typename Derived>
class Base {
public:
void foo() {
// 正确:使用 static_cast
auto& derived = static_cast<Derived&>(*this);
derived.bar();
// 错误:不要使用 dynamic_cast
// auto* derived = dynamic_cast<Derived*>(this);
}
};
5.2、 提供保护性析构函数
template <typename Derived>
class Base {
protected:
~Base() = default; // 防止通过基类指针删除
public:
// 其他接口
};
5.3、 使用 CRTP 辅助宏(可选)
#define CRTP_BASE(Derived) \\
Derived& derived() { return static_cast<Derived&>(*this); } \\
const Derived& derived() const { return static_cast<const Derived&>(*this); }
template <typename Derived>
class Base {
public:
CRTP_BASE(Derived)
void interface() {
derived().implementation();
}
};
6、 CRTP 的局限性
7、 实际项目中的 CRTP 示例
7.1 、访问者模式实现
template <typename Derived>
class Element {
public:
template <typename Visitor>
void accept(Visitor& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
class ConcreteElementA : public Element<ConcreteElementA> {
// 具体元素实现
};
class ConcreteElementB : public Element<ConcreteElementB> {
// 具体元素实现
};
7.2、 序列化框架
template <typename Derived>
class Serializable {
public:
std::string toJson() const {
const auto& derived = static_cast<const Derived&>(*this);
// 使用 derived 的字段生成 JSON
return "{}"; // 简化示例
}
void fromJson(const std::string& json) {
auto& derived = static_cast<Derived&>(*this);
// 从 JSON 解析并设置 derived 的字段
}
};
8、总结
CRTP 是 C++ 模板元编程中的强大工具,它通过编译时多态提供了零开销的抽象能力。虽然学习曲线较陡,但在性能关键的场景中,CRTP 可以显著提升程序效率。
适用场景:
- 需要零运行时开销的多态
- 编译时确定的类型关系
- 高性能库和框架开发
不适用场景:
- 需要运行时动态类型
- 简单的继承关系
- 对编译时间敏感的项目
二、代码示例
1、示例代码
#include <iostream>
#include <memory>
#include <string>
// ==============================================
// 一、基础CRTP骨架:静态多态分发(替代虚函数,0运行时开销)
// ==============================================
template <typename Derived>
class CRTPBase
{
public:
// 统一对外接口
void process()
{
// 安全向下转型,编译期确定调用派生类方法
static_cast<Derived*>(this)->impl_process();
}
void info() const
{
static_cast<const Derived*>(this)->impl_info();
}
protected:
// 保护析构,只允许派生类销毁基类
~CRTPBase() = default;
};
// 派生类1
class FileWriter : public CRTPBase<FileWriter>
{
public:
void impl_process()
{
std::cout << "CRTP静态分发:文件写入数据\\n";
}
void impl_info() const
{
std::cout << "类型:文件写入器\\n";
}
};
// 派生类2
class NetSender : public CRTPBase<NetSender>
{
public:
void impl_process()
{
std::cout << "CRTP静态分发:网络发送数据包\\n";
}
void impl_info() const
{
std::cout << "类型:网络发送器\\n";
}
};
// ==============================================
// 二、CRTP作为Mixin混入:给派生类批量附加能力
// 特性1:每个派生类独立静态成员变量(互不干扰)
// 特性2:自动对象计数
// ==============================================
template <typename Derived>
class ObjectCounter
{
public:
ObjectCounter() { ++s_objCount; }
ObjectCounter(const ObjectCounter&) { ++s_objCount; }
~ObjectCounter() { —s_objCount; }
// 静态方法查看当前实例数
static size_t GetInstanceCount()
{
return s_objCount;
}
private:
// 模板不同实例拥有独立静态变量
static size_t s_objCount;
};
// 静态变量初始化
template <typename Derived>
size_t ObjectCounter<Derived>::s_objCount = 0;
// 两个不同类,计数器完全隔离
class Button : public ObjectCounter<Button> {};
class Window : public ObjectCounter<Window> {};
// ==============================================
// 三、CRTP 编译期强制派生类必须实现指定接口(静态检查)
// 不实现则直接编译报错,替代运行时断言
// ==============================================
template <typename Derived>
class ForceInterface
{
public:
void run()
{
// 如果Derived没有impl_run,编译直接失败
static_cast<Derived*>(this)->impl_run();
}
protected:
~ForceInterface() = default;
};
class Task : public ForceInterface<Task>
{
public:
void impl_run()
{
std::cout << "强制接口:任务执行成功\\n";
}
};
// ==============================================
// 四、CRTP 实现流式链式调用
// ==============================================
template <typename Derived>
class Chainable
{
public:
Derived& Self()
{
return *static_cast<Derived*>(this);
}
};
class StringBuilder : public Chainable<StringBuilder>
{
private:
std::string m_buf;
public:
StringBuilder& Append(const std::string& str)
{
m_buf += str;
return Self();
}
void Print() const
{
std::cout << "链式拼接结果:" << m_buf << '\\n';
}
};
// ==============================================
// 五、CRTP 禁止拷贝/移动(模板方式统一禁用)
// ==============================================
template <typename Derived>
class NonCopyable
{
protected:
NonCopyable() = default;
~NonCopyable() = default;
// 删除拷贝构造、赋值、移动
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
NonCopyable(NonCopyable&&) = delete;
NonCopyable& operator=(NonCopyable&&) = delete;
};
class Resource : public NonCopyable<Resource>
{
public:
void Use()
{
std::cout << "独占资源使用中\\n";
}
};
// ==============================================
// 六、标准库经典CRTP应用:std::enable_shared_from_this
// ==============================================
class Node : public std::enable_shared_from_this<Node>
{
public:
std::shared_ptr<Node> GetSelf()
{
return shared_from_this();
}
void Hello()
{
std::cout << "enable_shared_from_this CRTP节点调用\\n";
}
};
// ==============================================
// 主函数统一测试所有CRTP能力
// ==============================================
int main()
{
std::cout << "========== 1. CRTP 静态多态分发 ==========\\n";
FileWriter fw;
NetSender ns;
fw.process();
fw.info();
ns.process();
ns.info();
std::cout << "\\n========== 2. CRTP Mixin 对象计数(独立静态变量) ==========\\n";
Button b1, b2;
Window w1;
std::cout << "Button实例数:" << Button::GetInstanceCount() << '\\n';
std::cout << "Window实例数:" << Window::GetInstanceCount() << '\\n';
std::cout << "\\n========== 3. CRTP 编译期强制接口实现 ==========\\n";
Task t;
t.run();
std::cout << "\\n========== 4. CRTP 链式调用 ==========\\n";
StringBuilder sb;
sb.Append("Hello ").Append("CRTP ").Append("Chain").Print();
std::cout << "\\n========== 5. CRTP 禁止拷贝资源类 ==========\\n";
Resource res;
res.Use();
// Resource res2 = res; // 编译报错,拷贝被删除
std::cout << "\\n========== 6. 标准库CRTP:enable_shared_from_this ==========\\n";
auto sp = std::make_shared<Node>();
auto sp2 = sp->GetSelf();
sp2->Hello();
return 0;
}
2、运行结果
========== 1. CRTP 静态多态分发 ==========
CRTP静态分发:文件写入数据
类型:文件写入器
CRTP静态分发:网络发送数据包
类型:网络发送器
========== 2. CRTP Mixin 对象计数(独立静态变量) ==========
Button实例数:2
Window实例数:1
========== 3. CRTP 编译期强制接口实现 ==========
强制接口:任务执行成功
========== 4. CRTP 链式调用 ==========
链式拼接结果:Hello CRTP Chain
========== 5. CRTP 禁止拷贝资源类 ==========
独占资源使用中
========== 6. 标准库CRTP:enable_shared_from_this ==========
enable_shared_from_this CRTP节点调用
D:\\user\\01417804\\桌面\\新建文件夹\\Project1\\x64\\Debug\\Project1.exe (进程 20716)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .




