奇异递归模板(CRTP)

奇异递归模板(CRTP)

一、什么是 CRTP?

奇异递归模板模式(Curiously Recurring Template Pattern)是 C++ 中的一种高级模板技巧,核心特征是:一个类派生自以自身作为模板参数的基类

代码实例

// 基类:模板类,接收派生类作为模板参数
template <typename Derived>
class Base {
public:
    // 基类中可调用派生类的成员
    void interface() {
        // 向下转型为派生类(安全,因为Derived必然是子类)
        static_cast<Derived*>(this)->implementation();
    }
};

// 派生类:继承基类,且将自身作为基类的模板参数
class Derived : public Base<Derived> {
public:
    // 派生类实现具体逻辑
    void implementation() {
        std::cout << "Derived的具体实现" << std::endl;
    }
};

这种 “递归” 看似怪异(派生类还未完全定义就作为基类参数),但 C++ 模板的 “延迟实例化” 特性使其合法 —— 模板代码直到被使用时才会实例化,此时派生类已完整定义。

二、CRTP 的核心价值

1. 静态多态(编译期多态)

替代虚函数实现 “多态行为”,但无需运行时虚函数表(vtable)开销,所有调用在编译期解析。

特性虚函数(动态多态)CRTP(静态多态)
解析时机运行时编译期
开销虚表查找 + 指针跳转无额外开销
灵活性运行时动态绑定编译期固定
适用场景运行时类型不确定编译期类型确定

2. 代码复用(策略提取)

将多个派生类的通用逻辑抽离到基类,避免重复编码,同时保证派生类的个性化实现。

3. 静态接口约束

基类可强制派生类实现特定方法(编译期检查),若派生类未实现,编译时直接报错。

三、应用场景实例

奇异递归模板(CRTP)结合单例模式核心是通过 CRTP 封装通用的单例逻辑,让任意业务类只需继承该 CRTP 基类即可快速实现单例,避免重复编写单例代码。

核心思路

  1. 用 CRTP 模板基类封装单例的通用逻辑(如实例创建、获取实例、禁止拷贝 / 赋值);
  2. 业务类继承该 CRTP 基类,并将自身作为模板参数,自动获得单例能力;
  3. 利用 C++11 及以上的特性保证线程安全(std::call_once/static局部变量)。

单例类的实现

template <typename T>
class Singleton {
protected:
    Singleton() = default;
    Singleton(const Singleton<T>&) = delete;
    Singleton& operator=(const Singleton<T>& st) = delete;
    static std::shared_ptr<T> _instance;
public:
    static std::shared_ptr<T> GetInstance() {
        static std::once_flag s_flag;
        std::call_once(s_flag, [&]() {
            _instance = std::shared_ptr<T>(new T);
            });
        return _instance;
    }
    void PrintAddress() {
        std::cout << _instance.get() << std::endl;
    }
    ~Singleton() {
        std::cout << "this is singleton destruct" << std::endl;
    }
};
template <typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;

逻辑线程中CRTP的使用

class HttpConnection;
typedef std::function<void(std::shared_ptr<HttpConnection>)> HttpHandler;

class LogicSystem : public Singleton<LogicSystem>
{
	friend class Singleton<LogicSystem>;
public:
	~LogicSystem();
	bool HandleGet(std::string, std::shared_ptr<HttpConnection>);
	void RegGet(std::string url, HttpHandler handler);
	void RegPost(std::string url, HttpHandler handler);
	bool HandlePost(std::string, std::shared_ptr<HttpConnection>);
private:
	LogicSystem();
	std::map<std::string, HttpHandler> _post_handlers;
	std::map<std::string, HttpHandler> _get_handlers;
};

四、CRTP 的注意事项

1. 避免裸指针转型风险

基类中必须用static_cast<Derived*>(this)而非dynamic_cast

  • dynamic_cast依赖虚函数表,CRTP 基类无虚函数,无法使用;
  • static_cast编译期完成,且因 CRTP 的语义约束(Derived 必为子类),转型安全。

2. 模板实例化时机

CRTP 基类的代码直到被调用时才实例化,若派生类未实现基类调用的方法,仅在调用时才会编译报错(而非继承时)。

3. 多重继承下的 CRTP

若多个 CRTP 基类依赖同一派生类,需确保模板参数唯一,避免二义性:

// 合法:多个CRTP基类,模板参数均为Derived
class Derived : public Base1<Derived>, public Base2<Derived> {};

4. 与虚函数的结合

CRTP 可与虚函数结合(虽不常见),但需注意:虚函数仍有运行时开销,CRTP 仅优化非虚部分。

五、CRTP 的核心原理

C++ 模板的 “两步实例化” 规则:

  1. 模板定义时,仅检查语法错误,不解析模板参数的具体类型;
  2. 模板实例化时(如Base<Derived>),才解析Derived的成员,此时Derived已完整定义。

因此,Base<Derived>static_cast<Derived*>(this)是合法的 —— 实例化时Derived已存在。

六、总结

CRTP 是 C++ 编译期优化的核心技巧,核心优势是零开销抽象

  • 替代虚函数实现多态,消除运行时开销;
  • 复用通用逻辑,同时保留派生类的个性化实现;
  • 编译期检查接口约束,提前暴露错误。
<think>好的,我现在需要帮助用户理解奇异递归模板模式(CRTP)的定义、实现和示例。用户已经提供了三个引用资料,我需要结合这些内容来组织回答。 首先,CRTP的定义。根据引用[1]和[2],CRTP是一种C++设计模式,基类模板将派生类作为模板参数。基类通过模板参数访问派生类的成员,实现静态多态。需要明确说明基类和派生类的关系,特别是自引用的特点。 接下来是实现部分。引用[3]中的代码示例展示了基类模板如何接受派生类作为参数,并在基类中使用static_cast将this转换为派生类指针。这样基类可以调用派生类的方法,或者派生类重写基类的函数。需要分步骤说明基类模板的定义、派生类的继承方式,以及如何通过static_cast实现类型转换。同时,引用[1]提到的链式调用可能也是实现的一个方面,可以结合说明。 然后是示例部分。需要提供一个简单的代码示例,展示CRTP的具体应用。比如基类有某个方法,派生类实现后,基类通过模板参数调用派生类的方法。比如计数器模式,基类记录对象数量,派生类继承并自动更新计数。或者像引用[3]中的print函数,派生类重写基类的print方法,基类调用时显示不同信息。 另外,需要注意用户提到的行内公式和独立公式的格式要求。不过目前看来,CRTP的内容可能不涉及数学公式,但如果有涉及的地方需要正确使用LaTeX格式。 用户还要求生成相关问题,我需要考虑CRTP的常见应用场景、优缺点、与其他模式的区别,以及实际项目中的使用案例等。 现在检查引用内容是否准确。引用[1]提到基类返回派生类引用,引用[2]说明基类模板接受派生类作为参数,引用[3]给出了具体的代码结构。需要确保这些内容在回答中被正确引用,并在适当的位置添加引用标识。 可能需要注意术语的一致性,比如CRTP的不同称呼,确保使用用户提供的资料中的术语。同时,解释CRTP如何实现静态多态,与动态多态的区别,以及其优势(如性能提升,编译时多态)。 最后,结构要清晰,分点回答定义、实现、示例,每个部分简明扼要,代码示例正确,并符合用户的格式要求。</think>### 奇异递归模板模式(CRTP)解析 #### 1. **定义** 奇异递归模板模式(Curiously Recurring Template Pattern, CRTP)是C++中一种**静态多态**设计模式,其核心思想是**基类模板以派生类作为模板参数**,从而实现基类对派生类成员的直接访问[^2]。 这种模式通过模板的自引用特性(基类依赖派生类的类型)实现编译时的多态行为,避免运行时虚函数开销[^3]。 --- #### 2. **实现** CRTP的实现分为以下关键步骤: 1. **基类模板定义**:基类模板接受一个类型参数`Derived`(实际为派生类)。 2. **派生类继承**:派生类通过`public Base<Derived>`递归继承基类模板。 3. **类型转换**:基类通过`static_cast<Derived*>(this)`将自身指针转换为派生类指针,调用派生类方法。 **代码示例(基础结构)**: ```cpp template <typename Derived> class Base { public: void interface() { static_cast<Derived*>(this)->implementation(); // 调用派生类方法 } }; class Derived : public Base<Derived> { public: void implementation() { /* 具体实现 */ } }; ``` --- #### 3. **示例:对象计数器** 以下示例通过CRTP统计某个类的实例数量: ```cpp template <typename T> class Counter { public: static int count; Counter() { ++count; } ~Counter() { --count; } static int getCount() { return count; } }; template <typename T> int Counter<T>::count = 0; // 派生类继承基类模板并将自身作为参数传递 class MyClass : public Counter<MyClass> { /* 类定义 */ }; // 使用 MyClass obj1, obj2; std::cout << MyClass::getCount(); // 输出2 ``` 此例中,`Counter`基类通过模板参数`T`区分不同派生类的计数器,实现编译时类型隔离。 --- #### 4. **核心优势** - **零运行时开销**:静态多态无需虚函数表(vtable)。 - **编译时多态**:类型检查和优化在编译阶段完成。 - **链式调用支持**:基类方法返回派生类引用,支持链式语法(如`obj.func1().func2()`)[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值