名字含义
字面的意思就是派生类名作为继承的模板的参数,形成了一种奇怪的递归。这个名字是由James O. Coplien提出来的,感兴趣的可以看一下原文(Curiously Recurring Template Patterns,但作者所写的FSM机制用的是抽象基类)。笔者认为,CRTP主要还是运用于静态多态。更多的可以翻墙看一下wiki。
主要用途
主要应用于要进行扩展的代码(大型代码分开写为了便于维护),而对此又要顾及效率问题。
如果实现算法仅仅是一种,且并不需要额外的扩展,那么就没有必要提及多态的运用(包括动态或者静态的多态),仅仅按照最简单的构建类就可以了。
例如可以把数据放在一个基类,把实现放在派生类。实现这样的情况可以有两种方法。
- 使用抽象基类,则运用同样的数据,然后运用相同的接口来进行不同派生类的方法实现,但此时就会构建虚函数表。
- 如果为了避免使用虚函数表,则就可以运用静态多态,构建一个继承于数据基类的接口基类模板,然后运用派生类继承接口基类模板来进行方法实现,此时就没有用到虚函数表,从而效率会有很大程度的提高,但也会造成编译时间过长,代码膨胀的缺点。
动态多态运行时造成消耗的原因
- 虚函数表,调用虚方法时要额外的指针解除引用
- 虚函数无法内联,对于小方法是个损耗
- 每个对象都要附加指针。如果是小对象,是个大损耗
实现方式
方法一(仅作为事例)
template <class T>
class Base {
public:
void interface() {
// ...
static_cast<T*>(this)->implementation();//向下转换指针类型进行调用
// ...
}
static void static_func() {
// ...
T::static_sub_func();
// ...
}
};
class Derived : Base<Derived> {
public:
void implementation();
static void static_sub_func();
};
方法二(仅作为示例)
template <typename Implementation>
class CRTPInterface {
public:
void tick(uint64_t n) {
impl().tick(n);
}
uint64_t getvalue() {
return impl().getvalue();
}
private:
Implementation& impl() {
return *static_cast<Implementation*>(this);//把类型转换放在私有部分,调用部分在公有实现
}
};
class CRTPImplementation : public CRTPInterface<CRTPImplementation> {
uint64_t counter;
public:
CRTPImplementation()
: counter(0) {
}
void tick(uint64_t n) {
counter += n;
}
uint64_t getvalue() {
return counter;
}
};
参考
- The Curiously Recurring Template Pattern in C++ ,Eli Bendersky’s website
- The cost of dynamic (virtual calls) vs. static (CRTP) dispatch in C++,Eli Bendersky’s website
- 奇异递归模板模式( Curiously Recurring Template Pattern,CRTP)1,DanielWang_
- 关于c++ template多态——CRTP 模式
- C++: Polymorphic cloning and the CRTP (Curiously Recurring Template Pattern),Katy