What is the curiously recurring template pattern (CRTP)?
如果不参考一本书,有人能用一个代码示例为
简而言之,crtp是当类A有一个基类,它是类A本身的模板专门化。例如。
1 2 3 | template <class T> class X{...}; class A : public X<A> {...}; |
这是奇怪的反复出现,不是吗?:)
现在,这给了你什么?这实际上使X模板能够成为其专门化的基类。
例如,您可以创建这样的通用单例类(简化版本)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; |
现在,为了使一个任意的类成为一个单例,您应该这样做
1 2 3 4 | class A: public Singleton<A> { //Rest of functionality for class A }; |
你明白了吗?singleton模板假定它对任何类型x的专门化将从
另一个有用的例子是Boost(我不确定他们是如何实现它的,但是CRTP也会实现)。假设您只想为类提供operator<而自动为它们提供operator==!
你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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, isnit it? Derived const& d2 = static_cast<Derived const&>(op2); return !(d1 < d2) && !(d2 < d1);//assuming derived has operator < } |
现在你可以这样用了
1 2 3 4 5 6 7 8 9 | struct Apple:public Equality<Apple> { int size; }; bool operator < (Apple const & a1, Apple const& a2) { return a1.size < a2.size; } |
现在,您还没有为Apple显式提供operator==但你有!你可以写
1 2 3 4 5 6 7 8 9 10 11 | int main() { Apple a1; Apple a2; a1.size = 10; a2.size = 10; if(a1 == a2) //the compiler won't complain! { } } |
如果您只为Apple编写operator==的话,这似乎会减少您的编写量,但是可以想象,equality模板将不仅提供==而且提供>、>=、<=等,并且您可以将这些定义用于多个类,重用代码!
CRTP是一件很好的事情。)
这里你可以看到一个很好的例子。如果使用虚拟方法,程序将知道在运行时执行什么。实现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 | template <class T> class Writer { public: Writer() { } ~Writer() { } void write(const char* str) const { static_cast<const T*>(this)->writeImpl(str); //here the magic is!!! } }; class FileWriter : public Writer<FileWriter> { public: FileWriter(FILE* aFile) { mFile = aFile; } ~FileWriter() { fclose(mFile); } //here comes the implementation of the write method on the subclass void writeImpl(const char* str) const { fprintf(mFile,"%s ", str); } private: FILE* mFile; }; class ConsoleWriter : public Writer<ConsoleWriter> { public: ConsoleWriter() { } ~ConsoleWriter() { } void writeImpl(const char* str) const { printf("%s ", str); } }; |
CRTP是一种实现编译时多态性的技术。下面是一个非常简单的例子。在下面的示例中,
http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e
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 | template <typename T> struct Base { void foo() { (static_cast<T*>(this))->foo(); } }; struct Derived : public Base<Derived> { void foo() { cout <<"derived foo" << endl; } }; struct AnotherDerived : public Base<AnotherDerived> { void foo() { cout <<"AnotherDerived foo" << endl; } }; template<typename T> void ProcessFoo(Base<T>* b) { b->foo(); } int main() { Derived d1; AnotherDerived d2; ProcessFoo(&d1); ProcessFoo(&d2); return 0; } |
输出:
1 2 | derived foo AnotherDerived foo |
正如注意:
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 | #pragma once #include <iostream> template <typename T> class Base { public: void method() { static_cast<T*>(this)->method(); } }; class Derived1 : public Base<Derived1> { public: void method() { std::cout <<"Derived1 method" << std::endl; } }; class Derived2 : public Base<Derived2> { public: void method() { std::cout <<"Derived2 method" << std::endl; } }; #include"crtp.h" int main() { Derived1 d1; Derived2 d2; d1.method(); d2.method(); return 0; } |
输出将是:
1 2 | Derived1 method Derived2 method |
这不是一个直接的答案,而是CRTP如何有用的一个例子。
CRTP的一个很好的具体例子是来自C++ 11的
[util.smartptr.enab]/1
A class
T can inherit fromenable_-shared_-from_-this to inherit theshared_-from_-this member functions that obtain ashared_-ptr instance pointing to*this .
也就是说,从
当你需要给一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | struct Node; void process_node(const std::shared_ptr<Node> &); struct Node : std::enable_shared_from_this<Node> // CRTP { std::weak_ptr<Node> parent; std::vector<std::shared_ptr<Node>> children; void add_child(std::shared_ptr<Node> child) { process_node(shared_from_this()); // Shouldn't pass `this` directly. child->parent = weak_from_this(); // Ditto. children.push_back(std::move(child)); } }; |
你不能直接通过
1 2 3 4 5 6 7 8 9 10 | struct S { std::shared_ptr<S> get_shared() const { return std::shared_ptr<S>(this); } }; // Both shared_ptr think they're the only owner of S. // This invokes UB (double-free). std::shared_ptr<S> s1 = std::make_shared<S>(); std::shared_ptr<S> s2 = s1->get_shared(); assert(s2.use_count() == 1); |