返回首页   我的Github 

c++中的CRTP

最近在学习boost::beast的时候,注意到了在自定义parser的地方,有这么一种设计:

1template<bool isRequest>
2class custom_parser
3    : public basic_parser<isRequest, custom_parser<isRequest>>

根据上面的说法,这种设计模式叫做CRTP,中文wiki翻译成"奇异递归模版模式"。其实这严格来说并不是个递归(两种类型互相分别为子类和模版参数),不过确实给了我耳目一新的感觉。

那么,什么是CRTP呢?CRTP是这样的一种设计模式:

  1. 在需要使用类似于继承多态的地方使用
  2. 将基类实现成模版类,例如:A<Derived>
  3. 在派生类中,将派生类自己作为模版参数实体化基类模版,然后继承之:class B : public A<B>

本质上来说,CRTP是派生类对基类进行依赖注入的一种形式。

考虑如下的A->B A->C继承使用虚函数调用的情况:

 1#include <iostream>
 2
 3class A
 4{
 5	public:
 6		void call_func()
 7		{
 8			func();
 9		}
10
11		virtual void func(){}
12};
13
14class B : public A
15{
16	public:
17		virtual void func()
18		{
19			std::cout << "B.func" << std::endl;
20		}
21};
22
23class C : public A
24{
25	public:
26		virtual void func()
27		{
28			std::cout << "C.func" << std::endl;
29		}
30};
31
32int main(void)
33{
34	B b;
35	C c;
36	b.call_func();
37	c.call_func();
38	return 0;
39}

可能存在以下问题:

  • 使用了虚表。虚表在cache miss的时候是非常慢的
  • 每增加一个要转发到派生类的函数,就要多标记一次virtual
  • 在基类中无法访问派生类,无法将由于派生类不合要求产生的错误提前到编译期

将上面的例子改成使用CRTP后,是这样的:

 1#include <iostream>
 2
 3template <class Derived>
 4class A
 5{
 6	public:
 7		void call_func()
 8		{
 9			static_cast<Derived*>(this)->func();
10		}
11};
12
13class B : public A<B>
14{
15	public:
16		void func()
17		{
18			std::cout << "B.func" << std::endl;
19		}
20};
21
22class C : public A<C>
23{
24	public:
25		void func()
26		{
27			std::cout << "C.func" << std::endl;
28		}
29};
30
31int main(void)
32{
33	B b;
34	C c;
35
36	b.call_func();
37	c.call_func();
38	return 0;
39}

除此之外,使用CRTP要注意几个问题:

  1. 要将this转换成Derived *访问派生类成员,静态成员可以直接访问
  2. 派生类并不能方便地cast成基类,不适用于依赖继承树多态的情况
  3. 如果要访问protected或者private成员需要声明友元
 我的Github