CRTP奇异递归模板模式学习

转自: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;
}

<think>好的,我现在需要帮助用户理解奇异递归模板模式CRTP)的定义、实现和示例。用户已经提供了三个引用资料,我需要结合这些内容来组织回答。 首先,CRTP的定义。根据引用[1]和[2],CRTP是一种C++设计模式,基类模板将派生类作为模板参数。基类通过模板参数访问派生类的成员,实现静态多态。需要明确说明基类和派生类的关系,特别是自引用的特点。 接下来是实现部分。引用[3]中的代码示例展示了基类模板如何接受派生类作为参数,并在基类中使用static_cast将this转换为派生类指针。这样基类可以调用派生类的方法,或者派生类重写基类的函数。需要分步骤说明基类模板的定义、派生类的继承方式,以及如何通过static_cast实现类型转换。同时,引用[1]提到的链式调用可能也是实现的一个方面,可以结合说明。 然后是示例部分。需要提供一个简单的代码示例,展示CRTP的具体应用。比如基类有某个方法,派生类实现后,基类通过模板参数调用派生类的方法。比如计数器模式,基类记录对象数量,派生类继承并自动更新计数。或者像引用[3]中的print函数,派生类重写基类的print方法,基类调用时显示不同信息。 另外,需要注意用户提到的行内公式和独立公式的格式要求。不过目前看来,CRTP的内容可能不涉及数学公式,但如果有涉及的地方需要正确使用LaTeX格式。 用户还要求生成相关问题,我需要考虑CRTP的常见应用场景、优缺点、与其他模式的区别,以及实际项目中的使用案例等。 现在检查引用内容是否准确。引用[1]提到基类返回派生类引用,引用[2]说明基类模板接受派生类作为参数,引用[3]给出了具体的代码结构。需要确保这些内容在回答中被正确引用,并在适当的位置添加引用标识。 可能需要注意术语的一致性,比如CRTP的不同称呼,确保使用用户提供的资料中的术语。同时,解释CRTP如何实现静态多态,与动态多态的区别,以及其优势(如性能提升,编译时多态)。 最后,结构要清晰,分点回答定义、实现、示例,每个部分简明扼要,代码示例正确,并符合用户的格式要求。</think>### 奇异递归模板模式CRTP)解析 #### 1. **定义** 奇异递归模板模式(Curiously Recurring Template Pattern, CRTP)是C++中一种**静态多态**设计模式,其核心思想是**基类模板以派生类作为模板参数**,从而实现基类对派生类成员的直接访问[^2]。 这种模式通过模板的自引用特性(基类依赖派生类的类型)实现编译时的多态行为,避免运行时虚函数开销[^3]。 --- #### 2. **实现** CRTP的实现分为以下关键步骤: 1. **基类模板定义**:基类模板接受一个类型参数`Derived`(实际为派生类)。 2. **派生类继承**:派生类通过`public Base<Derived>`递归继承基类模板。 3. **类型转换**:基类通过`static_cast<Derived*>(this)`将自身指针转换为派生类指针,调用派生类方法。 **代码示例(基础结构)**: ```cpp template <typename Derived> class Base { public: void interface() { static_cast<Derived*>(this)->implementation(); // 调用派生类方法 } }; class Derived : public Base<Derived> { public: void implementation() { /* 具体实现 */ } }; ``` --- #### 3. **示例:对象计数器** 以下示例通过CRTP统计某个类的实例数量: ```cpp template <typename T> class Counter { public: static int count; Counter() { ++count; } ~Counter() { --count; } static int getCount() { return count; } }; template <typename T> int Counter<T>::count = 0; // 派生类继承基类模板并将自身作为参数传递 class MyClass : public Counter<MyClass> { /* 类定义 */ }; // 使用 MyClass obj1, obj2; std::cout << MyClass::getCount(); // 输出2 ``` 此例中,`Counter`基类通过模板参数`T`区分不同派生类的计数器,实现编译时类型隔离。 --- #### 4. **核心优势** - **零运行时开销**:静态多态无需虚函数表(vtable)。 - **编译时多态**:类型检查和优化在编译阶段完成。 - **链式调用支持**:基类方法返回派生类引用,支持链式语法(如`obj.func1().func2()`)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值