news 2026/6/24 5:27:09

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
架构设计 - 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:编译期多态,高效但缺乏运行期灵活性。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/20 9:57:29

Z-Image-Turbo极速出图实战:6秒生成,成本低至1毛

Z-Image-Turbo极速出图实战&#xff1a;6秒生成&#xff0c;成本低至1毛 你是不是也经常为短视频封面发愁&#xff1f;每天要产出几十条内容&#xff0c;每一条都得配一张吸睛的封面图。以前靠手动设计&#xff0c;PS一顿操作猛如虎&#xff0c;结果一小时才出一张图&#xff…

作者头像 李华
网站建设 2026/6/18 1:20:23

8GB内存电脑跑LoRA:云端GPU加持,性能提升10倍

8GB内存电脑跑LoRA&#xff1a;云端GPU加持&#xff0c;性能提升10倍 你是不是也有一台老旧笔记本&#xff0c;想尝试AI模型微调&#xff0c;却被“训练太慢”劝退&#xff1f;本地用LoRA训练一个epoch要8小时&#xff0c;风扇狂转、系统卡顿&#xff0c;结果还经常崩溃。别急…

作者头像 李华
网站建设 2026/6/23 17:14:59

翻译大模型省钱攻略:Hunyuan-MT-7B云端按需付费,省90%成本

翻译大模型省钱攻略&#xff1a;Hunyuan-MT-7B云端按需付费&#xff0c;省90%成本 你是不是也遇到过这样的情况&#xff1f;公司业务要拓展海外&#xff0c;文档、合同、邮件、会议记录都需要高质量翻译&#xff0c;但市面上的翻译API用起来贵得离谱&#xff0c;按字计费一不小…

作者头像 李华
网站建设 2026/6/16 5:25:15

C++:实现多路复用epoll模型实例(附带源码)

一、项目背景详细介绍在传统的网络编程中&#xff0c;最直观的服务器模型通常是&#xff1a;一个客户端&#xff0c;一个线程或者 阻塞式顺序处理这种模型在客户端数量较少时尚可接受&#xff0c;但一旦并发连接数上升&#xff0c;就会暴露出严重问题&#xff1a;线程数量爆炸上…

作者头像 李华
网站建设 2026/6/16 5:18:42

FSMN VAD错误日志:lsof与kill命令停止服务操作详解

FSMN VAD错误日志&#xff1a;lsof与kill命令停止服务操作详解 1. 背景与问题引入 在部署基于阿里达摩院FunASR的FSMN VAD语音活动检测系统时&#xff0c;用户常通过run.sh脚本启动WebUI服务。默认情况下&#xff0c;该服务运行在7860端口&#xff0c;可通过浏览器访问http:/…

作者头像 李华