奇异递归模板模式(CRTR)
2026/5/11 17:56:42 网站建设 项目流程

文章目录

    • 奇异递归模板模式(CRTR)
      • 概念
      • CRTP vs 虚函数继承
        • 基本区别
        • CRTP的优势
        • CRTP的劣势
        • 虚函数继承的优势
        • 虚函数继承的劣势
        • 选择建议
      • 在 CRTR 模式下存储多种派生类类型的解决方案
        • 使用非模板的公共基类
        • 创建命令包装器

奇异递归模板模式(CRTR)

概念

奇异递归模板模式(Curiously Recurring Template Pattern,CRTR)的理念很简单:继承者将自身作为模板参数传递给基类。

classFoo:SomeBase<Foo>{//...};

通过这样的方式,可以在基类的实现中访问特定类型的 this 指针

假设基类 SomeBase 的每个派生类都实现了迭代所需的 begin()/end()接口,如何在基类 SomeBase 内部而不是派生类内部迭代对象?直接调用显然是不行的,因为基类没有 begin()/end() 的接口。

实现方式有两种:虚函数继承以及CRTR,这两种方式是有所差别的。

template<typenameDerived>classSomeBase{voidfool(){for(auto&item:*static_cast<Derived*>(this)){//...}}};classMyClass:SomeBase<MyClass>{classiterator{//your iterator defined here}iteratorbegin()const{...}iteratorend()const{...}};

当 MyClass 的实例调用 foo() 接口时,this 指针从 SomeBase*类型转换成 MyClass*类型,然后,解引用该指针,并在 range-based for loop 中实现迭代功能,其内部调用 MyClass::begin()和 MyClass::end()。

CRTP vs 虚函数继承

基本区别
  1. 多态实现机制:
    ○ CRTP:静态多态(编译时确定)
    ○ 虚函数:动态多态(运行时确定)
  2. 示例对比:
// CRTP方式(代码中的实现)template<typenameDerived>classCommand{public:voidexecute(conststd::string&args){static_cast<Derived*>(this)->executeImpl(args);}};// 虚函数方式classCommand{public:virtualvoidexecute(conststd::string&args)=0;virtual~Command(){}};
CRTP的优势
  1. 性能优化:
    ○ 无虚函数调用开销(无需查虚函数表)
    ○ 支持内联,允许更多编译器优化
    ○ 零运行时开销的抽象
  2. 编译时检查:
    ○ 类型安全,编译期捕获错误
    ○ 方法不存在时会产生编译错误
  3. 适用场景:
    ○ 高性能计算
    ○ 模板库设计
    ○ 嵌入式系统等资源受限环境
CRTP的劣势
  1. 代码膨胀:每个派生类都会实例化一套基类模板代码
  2. 编译时间:增加编译时间和编译复杂度
  3. 调试困难:模板错误提示往往难以理解
  4. 接口不明确:缺少明确的接口约束机制
  5. 不支持动态加载:无法支持运行时动态库加载等场景
虚函数继承的优势
  1. 运行时灵活性:
    ○ 支持运行时确定的多态行为
    ○ 可以处理编译时未知的类型
  2. 接口清晰:
    ○ 明确定义接口,强制派生类实现
    ○ 符合大多数开发者的OOP直觉
  3. 支持动态加载:可与动态链接库配合使用
  4. 适用场景:
    ○ 插件系统
    ○ 框架设计
    ○ 需要运行时类型确定的场景
虚函数继承的劣势
  1. 性能开销:
    ○ 虚函数调用的间接寻址开销
    ○ 每个对象额外的vptr(虚表指针)开销
  2. 内联限制:虚函数通常不能被内联
  3. 内存开销:虚函数表消耗额外内存
选择建议

📢根据具体需求选择:

需要极致性能且类型在编译时已知:选CRTP

需要运行时灵活性和动态行为:选虚函数继承

在 CRTR 模式下存储多种派生类类型的解决方案

使用非模板的公共基类

简单直接,只需少量修改现有代码。考虑简单实用使用该方案。

// 普通基类classCommandBase{public:virtualvoidexecute(conststd::string&args)=0;virtual~CommandBase()=default;};// 修改CRTP模板类template<typenameDerived>classCommand:publicCommandBase{public:voidexecute(conststd::string&args)override{static_cast<Derived*>(this)->executeImpl(args);}};// 使用方式std::unordered_map<std::string,std::unique_ptr<CommandBase>>commands;commands["add"]=std::make_unique<AddCommand>(taskManager);commands["delete"]=std::make_unique<DeleteCommand>(taskManager);// 调用commands["add"]->execute("任务描述,1,2023-12-31");
创建命令包装器

保持 CRTR 的性能优势,并提供类型安全的接口。考虑性能使用该方案。

classCommandWrapper{structConcept{virtualvoidexecute(conststd::string&args)=0;virtual~Concept()=default;};template<typenameT>structModel:Concept{T command;Model(T cmd):command(std::move(cmd)){}voidexecute(conststd::string&args)override{command.execute(args);}};std::unique_ptr<Concept>impl;public:template<typenameT>CommandWrapper(T cmd):impl(std::make_unique<Model<T>>(std::move(cmd))){}voidexecute(conststd::string&args){impl->execute(args);}};// 使用方式std::unordered_map<std::string,CommandWrapper>commands;commands.emplace("add",AddCommand(taskManager));commands.emplace("delete",DeleteCommand(taskManager));// 调用commands["add"].execute("任务描述,1,2023-12-31");

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询