奇異重現模板模式（curiously recurring template pattern，CRTP）是 C 模板程式設計時的一種慣用法（idiom）：把派生類作為基類的模板引數。更一般地被稱作 Fbound polymorphism，是一類。 歷史 1980 年代作為被提出。於 1995 年稱之為 CRTP。在當時的早期 C 模板超程式設計已經有所出現。 一般形式 The Curiously Recurring Template Pattern CRTP templateclass T class Base methods within Base can use template to access members of Derived class Derived public BaseDerived 靜態多型 C 語言的多型，原本是用虛擬函式來實現的，屬於動態多型。安德烈亞歷山德雷斯庫在中提出了奇異重現模板模式，並稱之為靜態多型（static polymorphism）。 template class T struct Base void interface static_castTthisimplementation static void static_func Tstatic_sub_func struct Derived BaseDerived void implementation static void static_sub_func 基類模板利用了其成員函式體（即成員函式的實現）在宣告之後很久都不會被例項化（實際上只有被呼叫的模板類的成員函式才會被例項化），並利用了派生類的成員函式（透過）。 在上例中，BaseDerivedinterface ，雖然是在 struct Derived 之前就被聲明瞭，但未被編譯器例項化直至它被實際呼叫，這發生於 Derived 宣告之後，此時 Derivedimplementation 的宣告是已知的。 這種技術獲得了類似於虛擬函式的效果，並避免了動態多型的代價。也有人把 CRTP 稱為 模擬的動態繫結。 這種模式廣泛用於 Windows ATL 與 WTL 庫，以及 BoostIterator，BoostPython 或者 BoostSerialization 等庫中。 考慮一個基類，沒有虛擬函式，則它的成員函式能夠呼叫的其它成員函式，只能是屬於該基類自身。當從這個基類派生其它類時，派生類繼承了所有未被覆蓋（overridden）的基類的資料成員與成員函式。如果派生類呼叫了一個被繼承的基類的函式，而該函式又呼叫了其它成員函式，這些成員函式不可能是派生類中的派生或者覆蓋的成員函式。也就是說，基類中是看不到派生類的。但是，基類如果使用了 CRTP，則在編譯時派生類的覆蓋的函式可被選中呼叫。這效果相當於編譯時模擬了虛擬函式呼叫但避免了虛擬函式的尺寸與呼叫開銷（VTBL 結構與方法查詢、多繼承機制）等代價。但 CRTP 的缺點是不能在執行時做出動態繫結。 不透過虛擬函式機制，基類訪問派生類的私有或保護成員，需要把基類宣告為派生類的友元（friend）。如果一個類有多個基類都出現這種需求，宣告多個基類都是友元會很麻煩。一種解決技巧是在派生類之上再派生一個 accessor 類，顯然 accessor 類有權訪問派生類的保護函式；如果基類有權訪問 accessor 類，就可以間接呼叫派生類的保護成員了。這種方法被 boost 的多個庫使用，如：BoostPython 中的 def_visitor_access 和 BoostIterator 的 iterator_core_access。原理示例程式碼如下： templateclass DerivedT class Base private struct accessor DerivedT accessor 類沒有資料成員，只有一些靜態成員函式 static int foo DerivedT derived int DerivedTfn DeriveTdo_foo 獲取 DerivedTdo_foo 的成員函式指標 return derivedfn 透過成員函式指標的函式呼叫 accessor 類僅是 Base 類的成員型別，而沒有例項化為 Base 類的資料成員。 public DerivedT derived 該成員函式返回派生類的例項的引用 return static_castDerivedTthis int foo 該函式具體實現了業務功能 return accessorfoo thisderived struct Derived BaseDerived 派生類不需要任何特別的友元宣告 protected int do_foo 具體實現 return 1 例子 1：物件計數 統計一個類的例項物件建立與析構的資料。可以輕鬆地利用 CRTP 實現 template typename T struct counter static int objects_created static int objects_alive counter objects_created objects_alive counter const counter objects_created objects_alive protected counter objects should never be removed through pointers of this type objects_alive template typename T int counterTobjects_created 0 template typename T int counterTobjects_alive 0 class X counterX class Y counterY 例子 2：多型複製構造 當使用多型時，常需要基於基類指標建立物件的一份複製。常見辦法是增加 clone 虛擬函式在每一個派生類中。使用 CRTP，可以避免在派生類中增加這樣的虛擬函式。 Base class has a pure virtual function for cloning class Shape public virtual Shape virtual Shape clone const 0 This CRTP class implements clone for Derived template typename Derived class Shape_CRTP public Shape public virtual Shape clone const return new Derived static_castDerived constthis Nice macro which ensures correct CRTP usage define Derive_Shape_CRTP Type class Type public Shape_CRTPType Every derived class inherits from Shape_CRTP instead of Shape Derive_Shape_CRTP Square Derive_Shape_CRTP Circle This allows obtaining copies of squares circles or any other shapes by shapePtrclone 例子 3：不可派生的類 一個類如果不希望被繼承，類似於 Java 中的具有 finally 性質的類，這在 C 中可以用虛繼承來實現： templatetypename T class MakeFinally private MakeFinally 只有 MakeFinally 的友類才可以構造 MakeFinally MakeFinally friend T class MyClasspublic virtual MakeFinallyMyClassMyClass 是不可派生類 由於虛繼承，所以 D 要直接負責構造 MakeFinally 類，從而導致編譯報錯，所以 D 作為派生類是不合法的。 class D public MyClass 另外，如果 D 類沒有例項化物件，即沒有被使用，實際上 D 類是被編譯器忽略掉而不報錯 int main MyClass var1 D var2 這一行編譯將導致錯誤，因為 D 類的預設建構函式不合法 例子 4：stdenable_shared_from_this 在 C 標準庫標頭檔案 memory 中，stdshared_ptr 類封裝了可被共享使用的指標或資源。一個被共享的物件不能直接把自身的原始指標（raw pointer）this 傳遞給 stdshared_ptr 的容器物件（如一個 stdvector），因為這會生成該被共享的物件的額外的共享指標控制塊。為此，stdshared_ptr API 提供了一種類模板設施 stdenable_shared_from_this，包含了成員函式 shared_from_this，從而允許從 this 建立一個 stdshared_ptr 物件。 class mySharedClasspublic stdenable_shared_from_thismySharedClass public int main stdvectorstdshared_ptrmySharedClass spv spvpush_back new mySharedClass stdshared_ptrmySharedClass p new mySharedClass mySharedClass cp spvemplace_back cshared_from_this 其它語言 在 Java 與NET Framework 中，常見把一個類作為泛型超類或 interface 的型別引數。例如，下述 Java 類實現了標準庫中的泛型介面 Comparable public class Item implements ComparableItem private String name Override public int compareTo final Item other return namecompareTo othername 這保證了編譯時不會比較 Item 與其它不是 Item 的物件如 String。與 C 不同，Java 類不能使用不同的型別引數擴充套件一個超類兩次，也不能用不同的型別引數實現同一個 interface 兩次。這是由於 Java 實用了型別擦除處理泛型。例如，上例的 class Item 不能同時實現 ComparableItem 與 ComparableObject。詳見。 參見 參考文獻 Category 軟體設計模式 CategoryC