奇異遞迴模板模式

奇異遞迴模板模式：把派生類作為基類的模板引數。更一般地被稱作F-bound polymorphism，是一類。歷史.1980年代作為被提出。於1995年稱之為CRTP。在當時的早期C++模板超程式設計已經有所出現。一般形式.// The Curiously Recurring Template Pattern (CRTP)templateclass Base// methods within Base can use template to access members of Derivedclass Derived : public Base// ...靜態多型.C++語言的多型，原本是用虛擬函式來實現的，屬於動態多型。安德烈·亞歷山德雷斯庫在中提出了奇異遞迴模板模式，並稱之為靜態多型。templatestruct Basevoid interface()// ...static_castimplementation();static void static_func()// ...T::static_sub_func();struct Derived : Basevoid implementation();static void static_sub_func();基類模板利用了其成員函式體。在上例中，Base::interface()，雖然是在struct Derived之前就被聲明瞭，但未被編譯器例項化直至它被實際呼叫，這發生於Derived宣告之後，此時Derived::implementation()的宣告是已知的。這種技術獲得了類似於虛擬函式的效果，並避免了動態多型的代價。也有人把CRTP稱為“模擬的動態繫結”。這種模式廣泛用於Windows ATL與WTL庫，以及Boost.Iterator，Boost.Python或者Boost.Serialization等庫中。考慮一個基類，沒有虛擬函式，則它的成員函式能夠呼叫的其它成員函式，只能是屬於該基類自身。當從這個基類派生其它類時，派生類繼承了所有未被覆蓋等代價。但CRTP的缺點是不能在執行時做出動態繫結。不透過虛擬函式機制，基類訪問派生類的私有或保護成員，需要把基類宣告為派生類的友元。如果一個類有多個基類都出現這種需求，宣告多個基類都是友元會很麻煩。一種解決技巧是在派生類之上再派生一個accessor類，顯然accessor類有權訪問派生類的保護函式；如果基類有權訪問accessor類，就可以間接呼叫派生類的保護成員了。這種方法被boost的多個庫使用，如：Boost.Python中的def_visitor_access和Boost.Iterator的iterator_core_access。原理示例程式碼如下：template class Baseprivate:struct accessor : DerivedT{ // accessor類沒有資料成員，只有一些靜態成員函式static int foo(DerivedT&amp; derived)int (DerivedT::*fn)() = &amp;DeriveT::do_foo; //獲取DerivedT::do_foo的成員函式指標return (derived.*fn)(); // 透過成員函式指標的函式呼叫}; // accessor類僅是Base類的成員型別，而沒有例項化為Base類的資料成員。public:DerivedT&amp; derived() // 該成員函式返回派生類的例項的引用return static_cast(*this);int foo(){ // 該函式具體實現了業務功能return accessor::foo( this-&gt;derived());};struct Derived : Base // 派生類不需要任何特別的友元宣告protected:int do_foo()// ... 具體實現return 1;};例子1：物件計數.統計一個類的例項物件建立與析構的資料。可以輕鬆地利用CRTP實現:templatestruct counterstatic int objects_created;static int objects_alive;counter()++objects_created;++objects_alive;counter(const counter&amp;)++objects_created;++objects_alive;protected:~counter() // objects should never be removed through pointers of this type--objects_alive;};template ::objects_created( 0 );template ::objects_alive( 0 );class X : counter// ...class Y : counter// ...例子2：多型複製構造.當使用多型時，常需要基於基類指標建立物件的一份複製。常見辦法是增加clone虛擬函式在每一個派生類中。使用CRTP，可以避免在派生類中增加這樣的虛擬函式。// Base class has a pure virtual function for cloningclass Shape {public:virtual Shape *clone() const = 0;// This CRTP class implements clone() for Derivedtemplateclass Shape_CRTP : public Shape {public:virtual Shape *clone() const {return new Derived(static_cast(*this));};// Nice macro which ensures correct CRTP usage// Every derived class inherits from Shape_CRTP instead of ShapeDerive_Shape_CRTP(Square) {};Derive_Shape_CRTP(Circle) {};This allows obtaining copies of squares, circles or any other shapes by codice_1.例子3：不可派生的類.一個類如果不希望被繼承，類似於Java中的具有finally性質的類，這在C++中可以用虛繼承來實現：template class MakeFinally{private:MakeFinally(){}//只有MakeFinally的友類才可以構造MakeFinallyfriend T;class MyClass:public virtual MakeFinally{};//MyClass是不可派生類//由於虛繼承，所以D要直接負責構造MakeFinally類，從而導致編譯報錯，所以D作為派生類是不合法的。class D: public MyClass{};//另外，如果D類沒有例項化物件，即沒有被使用，實際上D類是被編譯器忽略掉而不報錯int main()MyClass var1;// D var2; //這一行編譯將導致錯誤，因為D類的預設建構函式不合法例子4：std::enable_shared_from_this.在C++標準庫標頭檔案codice_2中，codice_3類封裝了可被共享使用的指標或資源。一個被共享的物件不能直接把自身的原始指標，因為這會生成該被共享的物件的額外的共享指標控制塊。為此，codice_3 API提供了一種類模板設施codice_7，包含了成員函式codice_8，從而允許從this建立一個codice_3物件。class mySharedClass:public std::enable_shared_from_this{public:};int main()std::vector&lt;std::shared_ptr&lt;mySharedClass» spv;spv.push_back(new mySharedClass());std::shared_ptr p(new mySharedClass());mySharedClass &amp;c=*p;spv.emplace_back(c.shared_from_this());其它語言.在Java與.NET Framework中，常見把一個類作為泛型超類或interface的型別引數。例如，下述Java類實現了標準庫中的泛型介面codice_10:public class Item implements Comparable {private String name;@Overridepublic int compareTo(final Item other) {return name.compareTo(other.name);這保證了編譯時不會比較codice_11與其它不是codice_11的物件如codice_13。與C++不同，Java類不能使用不同的型別引數擴充套件一個超類兩次，也不能用不同的型別引數實現同一個interface兩次。這是由於Java實用了型別擦除處理泛型。例如，上例的class codice_11不能同時實現codice_15與codice_16。詳見。