三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

c++ CRTP模式的使用小结

c++ CRTP模式的使用小结

CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)是C++中一种高级的模板编程技术,它通过将派生类作为基类的模板参数来实现编译期多态。

1. CRTP的基本概念

基本结构

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

// 基类模板

template<typenameDerived>

classBase {

public:

voidinterface() {

// 将调用转发给派生类的实现

static_cast<Derived*>(this)->implementation();

}

voidstatic_interface() {

// 调用派生类的静态方法

Derived::static_implementation();

}

};

// 派生类

classDerived :publicBase<Derived> {// 关键:将自己作为模板参数

public:

voidimplementation() {

std::cout <<"Derived implementation"<< std::endl;

}

staticvoidstatic_implementation() {

std::cout <<"Derived static implementation"<< std::endl;

}

};

2. CRTP的核心原理

编译期多态

1

2

3

4

5

6

7

8

9

10

11

12

13

template<typenameDerived>

classBase {

public:

// 编译期多态:调用哪个implementation在编译时确定

voidfoo() {

static_cast<Derived*>(this)->implementation();

}

// 可以添加默认实现

voidbar() {

std::cout <<"Base default implementation"<< std::endl;

}

};

3. CRTP的常见应用

应用1:静态多态(编译期多态)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

template<typenameDerived>

classShape {

public:

voiddraw()const{

// 编译期调用派生类的具体实现

static_cast<constDerived*>(this)->draw_impl();

}

doublearea()const{

returnstatic_cast<constDerived*>(this)->area_impl();

}

};

classCircle :publicShape<Circle> {

private:

doubleradius;

public:

Circle(doubler) : radius(r) {}

// 实现基类期望的方法

voiddraw_impl()const{

std::cout <<"Drawing Circle with radius "<< radius << std::endl;

}

doublearea_impl()const{

return3.14159 * radius * radius;

}

};

classSquare :publicShape<Square> {

private:

doubleside;

public:

Square(doubles) : side(s) {}

voiddraw_impl()const{

std::cout <<"Drawing Square with side "<< side << std::endl;

}

doublearea_impl()const{

returnside * side;

}

};

// 使用

template<typenameT>

voidprocessShape(constShape<T>& shape) {

shape.draw();

std::cout <<"Area: "<< shape.area() << std::endl;

}

intmain() {

Circle circle(5.0);

Square square(4.0);

processShape(circle);// 编译时生成Circle版本

processShape(square);// 编译时生成Square版本

}

应用2:计数器模式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

// 统计某个类创建的对象数量

template<typenameT>

classCounter {

protected:

Counter() { ++count; }

Counter(constCounter&) { ++count; }

Counter(Counter&&) { ++count; }

~Counter() { --count; }

public:

staticintgetCount() {returncount; }

private:

staticintcount;

};

// 静态成员初始化

template<typenameT>

intCounter<T>::count = 0;

// 使用

classMyClass1 :publicCounter<MyClass1> {

// ...

};

classMyClass2 :publicCounter<MyClass2> {

// ...

};

intmain() {

MyClass1 a, b, c;

MyClass2 x, y;

std::cout <<"MyClass1 count: "<< MyClass1::getCount() << std::endl;// 3

std::cout <<"MyClass2 count: "<< MyClass2::getCount() << std::endl;// 2

// 注意:每个Counter实例都有自己的静态count

}

应用3:多态拷贝(虚拟构造函数)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

template<typenameDerived>

classCloneable {

public:

// 虚拟构造函数模式

Derived* clone()const{

returnnewDerived(static_cast<constDerived&>(*this));

}

protected:

// 防止直接实例化

Cloneable() =default;

~Cloneable() =default;

};

classConcreteClass :publicCloneable<ConcreteClass> {

public:

intvalue;

ConcreteClass(intv) : value(v) {}

// 自动获得clone()方法

// 可以调用clone()来创建副本

};

intmain() {

ConcreteClass obj1(42);

ConcreteClass* obj2 = obj1.clone();

std::cout << obj2->value << std::endl;// 42

deleteobj2;

}

应用4:静态接口检查

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

// 混入模式:为类添加功能

template<typenameDerived>

classComparable {

public:

booloperator==(constDerived& other)const{

return!(static_cast<constDerived&>(*this) < other) &&

!(other <static_cast<constDerived&>(*this));

}

booloperator!=(constDerived& other)const{

return!(*this== other);

}

booloperator<=(constDerived& other)const{

return!(other <static_cast<constDerived&>(*this));

}

booloperator>=(constDerived& other)const{

return!(static_cast<constDerived&>(*this) < other);

}

booloperator>(constDerived& other)const{

returnother <static_cast<constDerived&>(*this);

}

};

// 只需要实现operator<,自动获得所有比较操作

classMyInt :publicComparable<MyInt> {

public:

intvalue;

MyInt(intv) : value(v) {}

booloperator<(constMyInt& other)const{

returnvalue < other.value;

}

};

intmain() {

MyInt a(10), b(20), c(10);

std::cout << std::boolalpha;

std::cout << (a < b) << std::endl;// true

std::cout << (a == b) << std::endl;// false

std::cout << (a == c) << std::endl;// true

std::cout << (a != b) << std::endl;// true

std::cout << (a <= c) << std::endl;// true

}

4. CRTP的高级用法

访问派生类成员

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

template<typenameDerived>

classAccessDerived {

public:

voidprintInfo() {

Derived* derived =static_cast<Derived*>(this);

// 访问派生类的protected成员

std::cout <<"Value: "<< derived->value << std::endl;

// 调用派生类的protected方法

derived->protectedMethod();

}

protected:

// 基类可以提供一些默认实现

virtualvoidprotectedMethod() {

std::cout <<"Base protected method"<< std::endl;

}

};

classMyDerived :publicAccessDerived<MyDerived> {

friendclassAccessDerived<MyDerived>;// 允许基类访问protected成员

protected:

intvalue = 42;

voidprotectedMethod() override {

std::cout <<"Derived protected method"<< std::endl;

}

};

多级CRTP

template<typenameDerived>

classLevel1 {

public:

voidlevel1Method() {

std::cout <<"Level1 calling: ";

static_cast<Derived*>(this)->implement();

}

};

template<typenameDerived>

classLevel2 :publicLevel1<Derived> {

public:

voidlevel2Method() {

std::cout <<"Level2 calling: ";

static_cast<Derived*>(this)->implement();

}

};

classFinalClass :publicLevel2<FinalClass> {

public:

voidimplement() {

std::cout <<"FinalClass implementation"<< std::endl;

}

};

CRTP + 策略模式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// 策略接口

template<typenameT>

classSerializationStrategy {

public:

std::string serialize(constT& obj)const{

returnstatic_cast<constT*>(this)->serializeImpl();

}

};

// 具体策略

classJSONSerialization :publicSerializationStrategy<JSONSerialization> {

public:

std::string serializeImpl()const{

return"{ \"type\": \"json\" }";

}

};

classXMLSerialization :publicSerializationStrategy<XMLSerialization> {

public:

std::string serializeImpl()const{

return"<type>xml</type>";

}

};

// 使用策略的类

template<typenameSerializationStrategy>

classDataProcessor {

private:

SerializationStrategy serializer;

public:

std::string process() {

returnserializer.serialize(serializer);

}

};

6. CRTP的最佳实践

实践1:使用类型检查

1

2

3

4

5

6

7

8

9

template<typenameDerived>

classBase {

// 编译时检查:确保Derived是从Base<Derived>派生的

static_assert(std::is_base_of<Base<Derived>, Derived>::value,

"Derived must inherit from Base<Derived>");

// C++17的更简洁写法

static_assert(std::is_base_of_v<Base, Derived>);

};

实践2:保护构造函数

1

2

3

4

5

6

7

8

9

10

11

template<typenameDerived>

classBase {

protected:

// 防止直接实例化Base

Base() =default;

~Base() =default;

// 防止在堆上创建Base

void* operatornew(std::size_t) =delete;

voidoperatordelete(void*) =delete;

};

实践3:使用CRTP实现Mixin

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

// Mixin:为类添加功能

template<template<typename>class... Mixins>

classMixedClass :publicMixins<MixedClass<Mixins...>>... {

// 继承多个Mixin

};

// 定义Mixin

template<typenameDerived>

classPrintable {

public:

voidprint()const{

std::cout <<"Printing..."<< std::endl;

}

};

template<typenameDerived>

classSerializable {

public:

std::string serialize()const{

return"Serialized";

}

};

// 使用

usingMyClass = MixedClass<Printable, Serializable>;

7. CRTP在实际项目中的应用

示例:数学向量库

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

template<typenameDerived,typenameT>

classVectorExpression {

public:

size_tsize()const{

returnstatic_cast<constDerived*>(this)->size();

}

T operator[](size_ti)const{

returnstatic_cast<constDerived*>(this)->operator[](i);

}

// 延迟计算:表达式模板

Derived& operator+=(constVectorExpression& other) {

Derived& self = *static_cast<Derived*>(this);

for(size_ti = 0; i < size(); ++i) {

self[i] += other[i];

}

returnself;

}

};

template<typenameT>

classVector :publicVectorExpression<Vector<T>, T> {

private:

std::vector<T> data;

public:

Vector(size_tn, T val = T{}) : data(n, val) {}

size_tsize()const{returndata.size(); }

T operator[](size_ti)const{returndata[i]; }

T& operator[](size_ti) {returndata[i]; }

// 允许从任何VectorExpression构造

template<typenameE>

Vector(constVectorExpression<E, T>& expr) : data(expr.size()) {

for(size_ti = 0; i < expr.size(); ++i) {

data[i] = expr[i];

}

}

};

8. CRTP的局限性

  • 编译时绑定:不能在运行时改变行为
  • 代码膨胀:每个模板实例都会生成新代码
  • 复杂的错误信息:模板错误信息难以理解
  • 无法处理异质集合:需要知道具体类型
  • 循环依赖:基类需要知道派生类的完整定义

9. 何时使用CRTP

适合使用CRTP的情况:

  • 需要静态多态(性能关键)
  • 实现混入(Mixin)功能
  • 编译期策略选择
  • 为类族添加通用功能
  • 表达式模板

不适合使用CRTP的情况:

  • 需要运行时多态
  • 类型在运行时确定
  • 需要存储异质对象的容器
  • 项目对二进制大小敏感

总结

CRTP是C++模板元编程中的强大工具,它通过编译期多态提供了零开销的抽象能力。虽然学习曲线较陡,但在性能敏感的场景下,CRTP可以替代虚函数,提供更好的运行时性能。
记住CRTP的核心思想:基类通过static_cast将this指针转换为派生类指针,从而调用派生类的方法,所有这一切都在编译期完成。

← 返回列表