当前位置: 首页 > news >正文

架构设计 - CRTP 奇异递归模板模式

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

一、什么是 CRTP?
CRTP(Curiously Recurring Template Pattern)直译是 “奇异递归模板模式”,核心特征是:一个类 A 继承自一个模板类,而这个模板类的模板参数正是类 A 本身。CRTP 在 C++ 标准库(如std::enable_shared_from_this)、开源框架(如 Boost)中广泛使用,是高性能 C++ 开发的重要技巧。

1. 最基础的 CRTP 结构

先看一个极简示例,直观理解结构:

// 模板类(基类) template <typename Derived> class Base { public: void do_something() { // 向下转型:将基类指针/引用转为派生类类型 Derived& derived = static_cast<Derived&>(*this); // 调用派生类的具体实现 derived.implementation(); } }; // 派生类:继承自Base<Derived>(模板参数是自己) class Derived : public Base<Derived> { public: void implementation() { std::cout << "Derived的具体实现" << std::endl; } }; // 测试代码 int main() { Derived d; d.do_something(); // 输出:Derived的具体实现 return 0; }
2. CRTP 的核心逻辑
  • 基类模板通过模板参数 “感知” 派生类的类型;
  • 基类中通过static_cast<Derived&>(*this)安全地将自身转为派生类对象;
  • 从而可以调用派生类的成员函数 / 成员变量,实现编译期的多态(区别于运行期的虚函数多态)。

二、CRTP 的核心用途(解决什么问题?)

CRTP 的核心价值是用编译期静态绑定替代运行期动态绑定,避免虚函数的开销,同时实现 “复用代码 + 定制化实现”

用途 1:静态多态(替代虚函数)

虚函数的多态是运行期确定调用哪个函数(有 vtable 开销),而 CRTP 在编译期就能确定,效率更高。

#include <iostream> using namespace std; // 基类模板:定义通用逻辑 template <typename Derived> class Shape { public: // 通用接口:计算面积 double area() const { // 调用派生类的具体计算逻辑 return static_cast<const Derived*>(this)->calc_area(); } }; // 圆形:定制calc_area class Circle : public Shape<Circle> { private: double radius; public: Circle(double r) : radius(r) {} // 派生类的具体实现 double calc_area() const { return 3.14159 * radius * radius; } }; // 矩形:定制calc_area class Rectangle : public Shape<Rectangle> { private: double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calc_area() const { return width * height; } }; int main() { Circle c(2.0); Rectangle r(3.0, 4.0); // 统一接口调用,编译期确定调用哪个calc_area cout << "圆面积:" << c.area() << endl; // 输出:12.56636 cout << "矩形面积:" << r.area() << endl; // 输出:12 return 0; }
用途 2:实现 “混入(Mixin)” 功能

Mixin 是一种代码复用方式,CRTP 可以轻松实现 Mixin,给不同类批量添加通用功能(比如日志、计数、克隆)。

#include <iostream> #include <string> using namespace std; // Mixin:添加计数功能的CRTP基类 template <typename Derived> class Counter { private: static int count; // 静态变量:统计派生类对象数量 public: Counter() { count++; } ~Counter() { count--; } // 通用接口:获取当前对象数量 static int get_count() { return count; } }; // 静态变量初始化 template <typename Derived> int Counter<Derived>::count = 0; // 类A:混入计数功能 class A : public Counter<A> {}; // 类B:混入计数功能 class B : public Counter<B> {}; int main() { A a1, a2; B b1; cout << "A的对象数:" << A::get_count() << endl; // 输出:2 cout << "B的对象数:" << B::get_count() << endl; // 输出:1 { A a3; cout << "A的对象数:" << A::get_count() << endl; // 输出:3 } // a3析构 cout << "A的对象数:" << A::get_count() << endl; // 输出:2 return 0; }
用途 3:避免代码重复(静态多态的扩展)

比如实现 “可比较” 的类,CRTP 可以封装</>/==等比较逻辑,派生类只需实现核心的operator<:

template <typename Derived> class Comparable { public: // 封装通用比较逻辑 bool operator>(const Derived& other) const { return other < static_cast<const Derived&>(*this); } bool operator<=(const Derived& other) const { return !(static_cast<const Derived&>(*this) > other); } bool operator>=(const Derived& other) const { return !(static_cast<const Derived&>(*this) < other); } }; // 整数包装类:只需实现operator< class MyInt : public Comparable<MyInt> { private: int val; public: MyInt(int v) : val(v) {} // 核心比较逻辑 bool operator<(const MyInt& other) const { return val < other.val; } }; int main() { MyInt a(5), b(10); cout << (a > b) << endl; // 输出:0(false) cout << (a <= b) << endl; // 输出:1(true) return 0; }

三、CRTP 的关键注意事项

  1. 编译期确定类型:CRTP 的所有逻辑都在编译期完成,没有运行期开销,但也无法像虚函数那样 “动态绑定”(比如基类指针指向不同派生类对象);
  2. 转型安全性:必须确保模板参数是真正的派生类,否则 static_cast 会导致未定义行为;
  3. 与虚函数的区别:
    虚函数:运行期多态,灵活但有 vtable 开销;
    CRTP:编译期多态,高效但缺乏运行期灵活性。
http://www.jsqmd.com/news/250365/

相关文章:

  • Hunyuan MT1.8B翻译断句错误?格式保留功能启用教程
  • 4个语音识别神器推荐:预置镜像开箱即用,5块钱全体验
  • Stable Diffusion 3.5避坑指南:云端部署解决CUDA版本冲突
  • 基于改进下垂控制的微电网控制研究(Simulink仿真实现)
  • AI智能文档扫描仪参数详解:Canny边缘检测阈值设置建议
  • 照片级AI绘画!Z-Image-Turbo生成写实图像体验
  • 永磁同步电机PMSM六种DPWM调制技术-DPWM0 、DPWM1、DPWM2、DPWM3、DPWMMAX、DPWMMIN研究(Simulink仿真实现)
  • 【低压配电网】【对单相接地低压电网监测方案性能】在径向低压测试馈线上使用WLS状态估计器的性能,由于测量误差的随机性质,分析以蒙特卡洛方式进行(Matlab代码实现)
  • ES6对象方法简写:更简洁的代码写法
  • ACE-Step模型优势剖析:3.5B参数如何平衡质量与速度
  • TurboDiffusion为何快?SageSLA注意力机制深度解析
  • Z-Image-Turbo极速出图实战:6秒生成,成本低至1毛
  • IndexTTS-2方言支持体验:云端快速测试,无需本地资源
  • NotaGen节日营销:快速生成品牌定制圣诞音乐的秘诀
  • 2026 年程序员接单全指南:平台这么多,别再选错了
  • 8GB内存电脑跑LoRA:云端GPU加持,性能提升10倍
  • Qwen3-Embedding-4B成本分摊:多团队使用计量部署教程
  • MiniMax 开源了一个新的 Coding Agent 评测集,叫 OctoCodingBench,用以去评测 Coding Agent 在完成任务的过程中,有没有遵守规矩?
  • MiDaS开箱即用镜像:免去CUDA烦恼,5分钟部署
  • DeepSeek-OCR论文精读:用视觉压缩突破长文本处理瓶颈|基于DeepSeek-OCR-WEBUI实战
  • MiDaS深度解析:1元体验SOTA模型,技术小白也能懂
  • 基于改进粒子群算法的多无人机协同航迹规划(Matlab代码实现)
  • 4G 显存即可运行!免环境搭建的 AI 电商换装工具实操指南
  • 强烈安利9个AI论文工具,本科生轻松搞定论文写作!
  • UI-TARS-desktop案例解析:Qwen3-4B-Instruct在金融风控中的应用
  • Qwen-Image-Layered vs Photoshop:实测对比3种图层方案,2小时搞定选型
  • 程序员接单实用指南:平台选择、真实体验与避坑思路
  • 部署bge-large-zh-v1.5省心方案:云端GPU按小时计费,1块钱起
  • Open Interpreter物理仿真:数值计算脚本生成实战
  • Qwen3-1.7B模型加载异常?常见问题全解