转自:https://zhuanlan.zhihu.com/p/54945314
1.介绍
奇异递归模板模式(Curiously Recurring Template Pattern,CRTP),把派生类作为基类的模板参数。
用途:
- 静态多态(static polymorphism)
- 添加方法同时精简代码
例子:
template<typename Child>
class Animal
{
public:
void Run()
{
static_cast<Child*>(this)->Run(); // 将派生类转换为基类,由用户保证使用的正确。
cout<<"animal"<<endl;
}
};
class Dog :public Animal<Dog>
{
public:
void Run()
{
cout << "Dog Run" << endl;
}
};
class Cat :public Animal<Cat>
{
public:
void Run()
{
cout << "Cat Run" << endl;
}
};
template<typename T>
void Action(Animal<T> &animal) // 参数实际是基类
{
animal.Run();
}
int main()
{
Dog dog;
Action(dog);
Cat cat;
Action(cat);
return 0;
}
// 运行结果:
Dog Run
animal
Cat Run
animal
另外还有合并精简代码的用法,可以看上面的链接。
但这个模板参数只能用在方法里,不能用来定义基类的成员变量,如果上面的animal这样定义,则编译会有问题:
template<typename Child>
class Animal
{
public:
void Run()
{
static_cast<Child*>(this)->Run();
cout<<"animal"<<endl;
}
public:
Child c;
};
// 运行错误
note: definition of 'Dog' is not complete until the closing '}'
class Dog :public Animal<Dog>
因为每个 Animal 对象都包含一个 Child 类型的成员变量,这会导致无限递归的内存分配,递归定义。例如:
- Dog 继承自 Animal<Dog>
- Animal<Dog> 包含一个 Dog 成员
- 这个 Dog 又包含 Animal<Dog>
- 循环往复...
2.扩展
CRTP可以实现单例基类,由其他类扩展。
可以支持多个模板参数在方法中使用。
#include <iostream>
// CRTP with additional template parameters
template <typename Derived, typename DataType>
class Base {
public:
void process() {
static_cast<Derived*>(this)->processImpl();
}
void showDataType() {
std::cout << "Data type: " << typeid(DataType).name() << std::endl;
}
};
// 继承 Base,并指定 DataType 为 int
class Derived1 : public Base<Derived1, int> {
public:
void processImpl() {
std::cout << "Processing Derived1 with int\n";
}
};
// 继承 Base,并指定 DataType 为 double
class Derived2 : public Base<Derived2, double> {
public:
void processImpl() {
std::cout << "Processing Derived2 with double\n";
}
};
int main() {
Derived1 d1;
d1.process();
d1.showDataType();
Derived2 d2;
d2.process();
d2.showDataType();
return 0;
}
Derived参数是主要的,另外的模板参数是辅助。
3.策略设计模式
可以让 Base 接受策略类作为模板参数,让不同子类拥有不同的行为:
#include <iostream>
// 策略类
class StrategyA {
public:
void run() { std::cout << "Running Strategy A\n"; }
};
class StrategyB {
public:
void run() { std::cout << "Running Strategy B\n"; }
};
// CRTP with Strategy
template <typename Derived, typename Strategy>
class Base {
public:
void execute() {
strategy.run(); // 使用不同策略
}
private:
Strategy strategy;
};
// 具体子类
class DerivedA : public Base<DerivedA, StrategyA> {};
class DerivedB : public Base<DerivedB, StrategyB> {};
int main() {
DerivedA objA;
objA.execute(); // 输出: Running Strategy A
DerivedB objB;
objB.execute(); // 输出: Running Strategy B
return 0;
}