前言

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 this
	void 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;
}

更多推荐