简而言之,CRTP 是指类A 有一个基类,它是类A 本身的模板特化。例如
template <class T>
class X{...};
class A : public X<A> {...};
奇怪地反复出现,不是吗? :)
现在,这给了你什么?这实际上使X 模板能够成为其专业化的基类。
例如,您可以像这样制作一个通用的单例类(简化版)
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
private:
Singleton(){}
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
现在,为了使任意类 A 成为单例,您应该这样做
class A: public Singleton<A>
{
//Rest of functionality for class A
};
所以你看到了吗?单例模板假定其对任何类型 X 的特化将从 singleton<X> 继承,因此其所有(公共的、受保护的)成员都可以访问,包括 GetInstance! CRTP 还有其他有用的用途。例如,如果您想计算您的类当前存在的所有实例,但想将此逻辑封装在一个单独的模板中(具体类的想法非常简单 - 有一个静态变量,在 ctors 中递增,在 dtors 中递减)。尝试将其作为练习!
另一个有用的例子,对于 Boost(我不确定他们是如何实现它的,但 CRTP 也可以)。
想象一下,您只想为您的课程提供运算符<,但自动为它们提供运算符==!
你可以这样做:
template<class Derived>
class Equality
{
};
template <class Derived>
bool operator == (Equality<Derived> const& op1, Equality<Derived> const & op2)
{
Derived const& d1 = static_cast<Derived const&>(op1);//you assume this works
//because you know that the dynamic type will actually be your template parameter.
//wonderful, isn't it?
Derived const& d2 = static_cast<Derived const&>(op2);
return !(d1 < d2) && !(d2 < d1);//assuming derived has operator <
}
现在可以这样使用了
struct Apple:public Equality<Apple>
{
int size;
};
bool operator < (Apple const & a1, Apple const& a2)
{
return a1.size < a2.size;
}
现在,您还没有为Apple 明确提供运算符==?但你有它!你可以写
int main()
{
Apple a1;
Apple a2;
a1.size = 10;
a2.size = 10;
if(a1 == a2) //the compiler won't complain!
{
}
}
如果您只为Apple 编写运算符==,您可能会写得更少,但想象一下Equality 模板不仅会提供==,还会提供>、>=、@987654344 @ 等。您可以将这些定义用于多个类,重用代码!
CRTP 是个好东西 :) HTH