
_START_ARTICLE_
奇异递归模板模式
_START_SECTION_
历史
_START_PARAGRAPH_
1980年代作为F 界量化被提出。Jim Coplien于1995年称之为CRTP。在当时的早期C++模板超编程已经有所出现。
_START_SECTION_
一般形式
_START_PARAGRAPH_
// The Curiously Recurring Template Pattern (CRTP)template<class T>class Base{_NEWLINE_    // methods within Base can use template to access members of Derived};class Derived : public Base<Derived>{_NEWLINE_    // ...};
_START_SECTION_
静态多态
_START_PARAGRAPH_
Andrei Alexandrescu在Modern C++ Design中称之为静态多态（static polymorphism）。_NEWLINE_template <class T> _NEWLINE_struct Base{_NEWLINE_    void interface()_NEWLINE_    {_NEWLINE_        // ..._NEWLINE_        static_cast<T*>(this)->implementation();_NEWLINE_        // ..._NEWLINE_    }_NEWLINE__NEWLINE_    static void static_func()_NEWLINE_    {_NEWLINE_        // ..._NEWLINE_        T::static_sub_func();_NEWLINE_        // ..._NEWLINE_    }};struct Derived : Base<Derived>{_NEWLINE_    void implementation();_NEWLINE_    static void static_sub_func();};_NEWLINE_基类模板利用了其成员函数体（即成员函数的实现）将不被实例化直至声明很久之后（实际上只有被调用的模板类的成员函数才会被实例化）；并利用了派生类的成员，这是通过{{ilh|lang={{langname|Type conversion}}|lang-code=Type conversion|1=类型转化|2=类型转化|d=|nocat=}}。_NEWLINE_在上例中，Base<Derived>::interface()，虽然是在struct Derived之前就被声明了，但未被编译器实例化直至它被实际调用，这发生于Derived声明之后，此时Derived::implementation()的声明是已知的。_NEWLINE_这种技术获得了类似于虚函数的效果，并避免了动态多态的代价。也有人把CRTP称为“模拟的动态绑定”。_NEWLINE_这种模式广泛用于Windows ATL与WTL库，以及Boost.Iterator，Boost.Python或者Boost.Serialization等库中。_NEWLINE_考虑一个基类，没有虚函数，则它的成员函数能够调用的其它成员函数，只能是属于该基类自身。当从这个基类派生其它类时，派生类继承了所有未被覆盖（overridden）的基类的数据成员与成员函数。如果派生类调用了一个被继承的基类的函数，而该函数又调用了其它成员函数，这些成员函数不可能是派生类中的派生或者覆盖的成员函数。也就是说，基类中是看不到派生类的。但是，基类如果使用了CRTP，则在编译时派生类的覆盖的函数可被选中调用。这效果相当于编译时模拟了虚函数调用但避免了虚函数的尺寸与调用开销（VTBL结构与方法查找、多继承机制）等代价。但CRTP的缺点是不能在运行时做出动态绑定。_NEWLINE_不通过虚函数机制，基类访问派生类的私有或保护成员，需要把基类声明为派生类的友元（friend）。如果一个类有多个基类都出现这种需求，声明多个基类都是友元会很麻烦。一种解决技巧是在派生类之上再派生一个accessor类，显然accessor类有权访问派生类的保护函数；如果基类有权访问accessor类，就可以间接调用派生类的保护成员了。这种方法被boost的多个库使用，如：Boost.Python中的def_visitor_access和Boost.Iterator的iterator_core_access。原理示例代码如下：_NEWLINE_template<class DerivedT> class Base{_NEWLINE_  private:_NEWLINE_    struct accessor : DerivedT  _NEWLINE_    {                                      // accessor类没有数据成员，只有一些静态成员函数_NEWLINE_        static int foo(DerivedT& derived)_NEWLINE_        {_NEWLINE_            int (DerivedT::*fn)() = &DeriveT::do_foo; //获取DerivedT::do_foo的成员函数指针_NEWLINE_            return (derived.*fn)();        // 通过成员函数指针的函数调用_NEWLINE_        }_NEWLINE_    };                                     // accessor类仅是Base类的成员类型，而没有实例化为Base类的数据成员。_NEWLINE_  public:_NEWLINE_    DerivedT& derived()                    // 该成员函数返回派生类的实例的引用_NEWLINE_    {_NEWLINE_       return static_cast<DerivedT&>(*this);_NEWLINE_    }_NEWLINE_    int foo()_NEWLINE_   {                                       //  该函数具体实现了业务功能_NEWLINE_        return accessor::foo( this->derived());_NEWLINE_    }};_NEWLINE_ _NEWLINE_struct Derived : Base<Derived>             //  派生类不需要任何特别的友元声明_NEWLINE_  protected:_NEWLINE_    int do_foo() _NEWLINE_    {_NEWLINE_         // ... 具体实现_NEWLINE_         return 1; _NEWLINE_     }};
_START_SECTION_
例子1：对象计数
_START_PARAGRAPH_
统计一个类的实例对象创建与析构的数据。This can be easily solved using CRTP:_NEWLINE_template <typename T>struct counter{_NEWLINE_    static int objects_created;_NEWLINE_    static int objects_alive;_NEWLINE__NEWLINE_    counter()_NEWLINE_    {_NEWLINE_        ++objects_created;_NEWLINE_        ++objects_alive;_NEWLINE_    }_NEWLINE_    _NEWLINE_    counter(const counter&)_NEWLINE_    {_NEWLINE_        ++objects_created;_NEWLINE_        ++objects_alive;_NEWLINE_    }protected:_NEWLINE_    ~counter() // objects should never be removed through pointers of this type_NEWLINE_    {_NEWLINE_        --objects_alive;_NEWLINE_    }};template <typename T> int counter<T>::objects_created( 0 );template <typename T> int counter<T>::objects_alive( 0 );class X : counter<X>{_NEWLINE_    // ...};class Y : counter<Y>{_NEWLINE_    // ...};
_START_SECTION_
例子2：多态复制构造
_START_PARAGRAPH_
当使用多态时，常需要基于基类指针创建对象的一份拷贝。常见办法是增加clone虚函数在每一个派生类中。使用CRTP，可以避免在派生类中增加这样的虚函数。 _NEWLINE_// Base class has a pure virtual function for cloningclass Shape {public:_NEWLINE_    virtual ~Shape() {}_NEWLINE_    virtual Shape *clone() const = 0;};// This CRTP class implements clone() for Derivedtemplate <typename Derived>class Shape_CRTP : public Shape {public:_NEWLINE_    virtual Shape *clone() const {_NEWLINE_        return new Derived(static_cast<Derived const&>(*this));_NEWLINE_    }};// Nice macro which ensures correct CRTP usage#define Derive_Shape_CRTP(Type) class Type: public Shape_CRTP<Type>// Every derived class inherits from Shape_CRTP instead of ShapeDerive_Shape_CRTP(Square) {};Derive_Shape_CRTP(Circle) {};_NEWLINE_This allows obtaining copies of squares, circles or any other shapes by shapePtr->clone().
_START_SECTION_
例子3：不可派生的类
_START_PARAGRAPH_
一个类如果不希望被继承，类似于Java中的具有finally性质的类，这在C++中可以用虚继承来实现：_NEWLINE_template<typename T> class MakeFinally{_NEWLINE_   private:_NEWLINE_       MakeFinally(){}//只有MakeFinally的友类才可以构造MakeFinally_NEWLINE_       ~MakeFinally(){}_NEWLINE_   friend T;};class MyClass:public virtual  MakeFinally<MyClass>{};//MyClass是不可派生类//由于虚继承，所以D要直接负责构造MakeFinally类，从而导致编译报错，所以D作为派生类是不合法的。class D: public MyClass{};//另外，如果D类没有实例化对象，即没有被使用，实际上D类是被编译器忽略掉而不报错int main(){MyClass var1;// D var2;  //这一行编译将导致错误，因为D类的默认构造函数不合法}
_START_SECTION_
例子4：std::enable_shared_from_this
_START_PARAGRAPH_
在C++标准库头文件<memory>中，std::shared_ptr类封装了可被共享使用的指针或资源。一个被共享的对象不能直接把自身的原始指针（raw pointer）this传递给std::shared_ptr的容器对象（如一个std::vector），因为这会生成该被共享的对象的额外的共享指针控制块。为此，std::shared_ptr API提供了一种类模板设施std::enable_shared_from_this，包含了成员函数shared_from_this，从而允许从this创建一个std::shared_ptr对象。_NEWLINE_class mySharedClass:public  std::enable_shared_from_this<mySharedClass>{public:_NEWLINE_  // ...};int main(){_NEWLINE_  std::vector<std::shared_ptr<mySharedClass>> spv;_NEWLINE_  spv.push_back(new mySharedClass());_NEWLINE_  std::shared_ptr<mySharedClass> p(new mySharedClass());_NEWLINE_  mySharedClass &c=*p;_NEWLINE_  spv.emplace_back(c.shared_from_this());}
_START_SECTION_
其它语言
_START_PARAGRAPH_
在Java与.NET Framework中，常见把一个类作为泛型超类或interface的类型参数。例如，下述Java类实现了标准库中的泛型界面Comparable:_NEWLINE_public class Item implements Comparable<Item> {_NEWLINE_private String name;_NEWLINE__NEWLINE_@Override_NEWLINE_public int compareTo(final Item other) {_NEWLINE_return name.compareTo(other.name);_NEWLINE_}}_NEWLINE_这保证了编译时不会比较Item与其它不是Item的对象如String。与C++不同，Java类不能使用不同的类型参数扩展一个超类两次，也不能用不同的类型参数实现同一个interface两次。这是由于Java实用了类型擦除处理泛型。例如，上例的class Item不能同时实现Comparable<Item>与Comparable<Object>。详见:en:Generics in Java § Problems with type erasure。