奇异递归模板模式(Curiously Recurring Template Pattern) - 知乎 (zhihu.com)
本文来自上面的文章!!!本菜鸡学习和记录一下。
CRTP是C++模板编程时的一种惯用法:把派生类作为基类的模板参数。
1.静态多态
#include <iostream>
using namespace std;
template <typename Child>
struct Base
{
void interface()
{
static_cast<Child*>(this)->implementation();
}
};
struct Derived : Base<Derived>
{
void implementation()
{
cerr << "Derived implementation\n";
}
};
struct test : Base<test>
{
void implementation()
{
cerr << "test\n";
}
};
int main()
{
Derived d;
d.interface(); // Prints "Derived implementation"
test t;
t.interface();
return 0;
}
基类为Base,是模板类,子类Drived继承自Base同时模板参数为Drived,基类中有接口
interface而子类中有接口对应实现implementation,基类interface中将this通过static_cast转换为模板参数类型,这里是Drived,并调用该类型的implemention方法。
为什么是static_cast,而不是dynamic_cast?
因为只有继承了Base的类型才能调用interface且这里是向下转型,所以采用static_cast是安全的。
(不太理解)
通过CRTP可以使得类具有类似于虚函数的效果,同时没有虚函数调用时的开销(虚函数调用时需要通过虚函数指针查找虚函数表进行调用),同时类的对象的体积相比使用虚函数也会减少(不需要存储虚函数指针),但是缺点是无法动态绑定。
2.
template<typename Child>
class Animal
{
public:
void Run()
{
static_cast<Child*>(this)->Run();
}
};
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继承自Animal且模板参数为Dog,Cat继承自Animal且模板参数为Cat
Animal,Dog,Cat中都声明了Run,Animal中的Run是通过类型转换后调用模板类型的Run方法实现的。在Action模板参数中接收Animal类型的引用(或指针)并在其中调用了animal对象的Run方法,由于这里传入的是不同的子类对象,因此Action中的animal也会有不同的行为。
3.添加方法,减少冗余
//Vec3
struct Vector3
{
float x;
float y;
float z;
Vector3() = default;
Vector3(float _x, float _y, float _z);
inline Vector3& operator+=(const Vector3& rhs);
inline Vector3& operator-=(const Vector3& rhs);
//....
};
inline Vector3 operator+(const Vector3& lhs, const Vector3& rhs);
inline Vector3 operator-(const Vector3& lhs, const Vector3& rhs);
//....
//Vec2
struct Vector2
{
float x;
float y;
Vector2() = default;
Vector2(float _x, float _y);
inline Vector2& operator+=(const Vector2& rhs);
inline Vector2& operator-=(const Vector2& rhs);
//....
};
inline Vector2 operator+(const Vector2& lhs, const Vector2& rhs);
inline Vector2 operator-(const Vector2& lhs, const Vector2& rhs);
//....
类型Vector3需要实现+=,-=,+,-等运算符重载。
类型Vector2需要实现+=,-=,+,-等运算符重载。
其中+=,-=这两个运算符可以采取+,-运算符实现,这时候可以把+=,-=给抽象出来,减少代码冗余。
template<typename T>
struct VectorBase
{
T& underlying() { return static_cast<T&>(*this); }
T const& underlying() const { return static_cast<T const&>(*this); }
inline T& operator+=(const T& rhs)
{
this->underlying() = this->underlying() + rhs;
return this->underlying();
}
inline T& operator-=(const T& rhs)
{
this->underlying() = this->underlying() - rhs;
return this->underlying();
}
//.....
};
struct Vector3 : public VectorBase<Vector3>
{
float x;
float y;
float z;
Vector3() = default;
Vector3(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
}
};
inline Vector3 operator+(const Vector3& lhs, const Vector3& rhs)
{
Vector3 result;
result.x = lhs.x + rhs.x;
result.y = lhs.y + rhs.y;
result.z = lhs.z + rhs.z;
return result;
}
inline Vector3 operator-(const Vector3& lhs, const Vector3& rhs)
{
Vector3 result;
result.x = lhs.x - rhs.x;
result.y = lhs.y - rhs.y;
result.z = lhs.z - rhs.z;
return result;
}
//......
int main()
{
Vector3 v0(6.0f, 5.0f, 4.0f);
Vector3 v2(4.0f, 5.0f, 6.0f);
v0 += v2;
v0 -= v2;
return 0;
}
在VectorBase中实现了+=,-=
它们依赖子类的+和-运算符的实现。