C++奇异递归模板模式(CRTP)
前言
C++奇异递归模板模式 (Curiously Recurring Template Pattern, CRTP) 是一种模板编程技术,其特点是派生类将自身作为基类模板的模板实参,使基类能够以静态多态的方式使用派生类的特性和方法,从而实现编译期的多态和代码复用。其一般形式是这样:
template <typename T>
class Base {
};class Derived : public Base<Derived> {// Derived 作为 Base 模板实参,同时又派生自 Base// 这样 Base 中可将 this 转换为 T 派生类型,访问其接口
};
CRTP在编译期间确定类型和行为,实现了静态多态性,减少了运行时开销,但是灵活性不如运行时多态,且模板调试起来也很麻烦。
应用场景
1.静态多态
通过基类接口调用派生类实现
template <typename Derived>
class Shape {
public:void draw() {static_cast<Derived *>(this)->drawImpl();}
};class Circle : public Shape<Circle> {
public:void drawImpl() { }
};
C++23的Deducing This特性(显式this参数)可以简化CRTP代码,不再需要显式的将派生类作为模板实参
class Shape {
public:// Deducing thisvoid draw(this auto& self) { self.drawImpl();}
};class Circle : public Shape {
public:void drawImpl() {}
};
2.mixin(功能注入)
mixin是一种代码复用模式,通过继承或组合将功能混入到某个类中,给类增加额外的行为或属性,行为可以参考Python接口装饰器
把可复用的功能模块注入类(has-a),而不是表达is-a关系
template <typename Derived>
class CountableMixin {
private:// C++17 inline内联初始化静态变量static inline int counter{ 0 };
public:CountableMixin() { ++counter; }~CountableMixin() { --counter; }static int count() { return counter; }
};class MyClass : public CountableMixin<MyClass> {// 自动获得计数功能
};
template <typename T>
struct Comparable {friend bool operator==(const T& a, const T& b) {return a.compare(b) == 0;}friend bool operator>(const T& a, const T& b) {return a.compare(b) > 0;}// ... ...
};// 多继承混合多种能力
class MyInt : public Comparable<MyInt>, ... ... {
public:MyInt(int v) : value(v) {}// 定义一个compare自动获得一系列比较运算符重载int compare(const MyInt& other) const {return value - other.value;}
private:int value;
};
3.模板方法模式
模板方法模式是一种行为设计模式,在父类中定义算法骨架,将一些步骤的实现延迟到子类
template <typename T>
class Base {
public:void algorithm() {static_cast<T*>(this)->step2();}void step1() {}
};class Derived : public Base<Derived> {
public:void step2() {}
};
此外工厂模式、策略模式等许多经典模式也可以用CRTP实现
4.std::enable_shared_from_this
C++标准库模板类std::enable_shared_from_this允许对象安全地获取指向自身的shared_ptr,避免从原始指针创建多个shared_ptr导致重复释放。其原理就是内部维护了一个weak_ptr,构造shared_ptr的时候会初始化这个weak_ptr。
#include <memory>
#include <iostream>struct MyItem : public std::enable_shared_from_this<MyItem>
{int value;std::shared_ptr<MyItem> getSharedPtr(){return shared_from_this();}
};int main()
{auto s1 = std::make_shared<MyItem>();auto s2 = s1->getSharedPtr();s1->value = 3;std::cout << "s2 value = " << s2->value << std::endl;return 0;
}