CRTP在项目优化中的使用
- 开源代码
- 2025-08-29 01:00:02

CRTP基本概念
CRTP 全称 Curiously Recurring Template Pattern,即奇异递归模板模式。是一种 C++ 编程技巧,使用模板类和继承的组合来实现静态多态。该模式的关键思想是:在模板类的定义中,模板参数是当前类自身(通常是派生类)。这个技巧通常用于实现编译时多态,优化性能,C++中std:: enable_shared_from_this 也是一种CRTP的实践。。一般代码形式如下:
// 先定义一个模板类作为基类 template <typename T> class Base { ... }; // 定义一个派生类,这个类继承以自身作为参数的基类 class Derived : public Base<Derived> { ... }; CRTP的基本原理基类模板利用了其成员函数体(即成员函数的实现)在声明之后很久都不会被实例化(实际上只有被调用的模板类的成员函数才会被实例化),并利用了派生类的成员函数(通过类型转化)。
假设当前需要设计一个 Analimal类中包含 getCharacters() 接口,有不同的子类对该接口进行实现,很容易想到的方法是利用 C++ 的多态机制,实现大概是这样:
class Analimal { virtual std::string getCharacters() = 0; }; class Cat: public Analimal { std::string getCharacters() override { return "Cat is a small animal..."; } }; class Dog : public Analimal { std::string getCharacters() override { return "Dog is a loaty anmial..."; } };这样是传统动态多态的实现方法,在运行时通过查询虚函数表,找到实际调用接口,返回正确的类名。
CRTP 的形式如何实现:
template <typename T> class Analimal { virtual std::string getCharacters() { // 强制转换为子类,调用子类的characters() return static_cast<T *>(this)->characters(); } }; class Cat: public Analimal<Cat> { std::string characters() override { return "Cat is a small animal..."; } }; class Dog : public Analimal<Cat> { std::string characters() override { return "Dog is a loaty anmial..."; } };基类在编译时就可以知道派生类的信息!因此,以前是虚函数调用,现在是在编译期就将模板与正确的函数进行绑定。(通过继承实现虚函数的功能,又没有了虚函数调用产生的开销),这种“虚调用”不会产生过多的开销。编译器在编译时就一直跟踪调用方法,甚至会内联它。
CRTP与传统动态多态对比特性
CRTP(静态多态)
动态多态
性能
高效,无运行时开销(内联优化可能性大)
有虚函数表查找开销,性能略低
灵活性
受限,类型在编译时固定
灵活,类型可以在运行时动态选择
类型安全性
高,编译时检查
低,存在类型转换失败风险
编译期 vs 运行期
完全在编译时
依赖运行时
耦合性
较高,A 模块使用 B 模块中的 CRTP 实现,涉及到的符号都得对 A 模块可见
较低,A 模块使用 B 模块中的接口类,接口实际实现的类不需要对 A 模块暴露
可读性
很差,涉及到模版,还存在代码体积膨胀问题
较差
使用场景 静态多态以以上Analimal的代码为例,实际使用时。
template <typename T> void getCharacterDsc(T& base){ base.getCharacters(); } int main(){ Dog d; Cat c; getCharacterDsc(d); getCharacterDsc(c); return 0; }定义了一个函数getCharacterDsc(),在其函数体内调用getCharacters()函数。如果类型为Dog和Cat,则会调用这俩类型对应的characters()函数。即不使用virtual,也实现了多态功能,其二者的区别是:virtual是运行时多态,而CRTP则是在编译期就对模板进行了实例化,所以属于静态多态。
代码复用现在需要实现一个功能,根据对象的具体类型,输出其类型名称。传统的动态多态写法如下,代码比较冗余。
#include <iostream> #include <typeinfo> class Base { public: virtual void PrintType() const { std::cout << typeid(*this).name() << std::endl; } }; class Derived : public Base { public: virtual void PrintType() const { std::cout << typeid(*this).name() << std::endl; } }; class Derived1 : public Base { public: virtual void PrintType() const { std::cout << typeid(*this).name() << std::endl; } }; void PrintType(const Base& base) { base.PrintType(); }使用CRTP则精简如下:
template<typename T> class Base { public: void PrintType() { T &t = static_cast<T&>(*this); std::cout << typeid(t).name() << std::endl; } }; class Derived : public Base<Derived> {}; class Derived1 : public Base<Derived1> {}; template<typename T> void PrintType(T base) { base.PrintType(); }CRTP在项目优化中的使用由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“CRTP在项目优化中的使用”