奇異重現模板模式

奇異重現模板模式（curiously recurring template pattern，CRTP）是C++模板編程時的一種慣用法（idiom）：把派生類作為基類的模板參數。更一般地被稱作F-bound polymorphism，是一類F 界量化。

**歷史**
1980年代作為F 界量化被提出。Jim Coplien於1995年稱之為CRTP。在當時的早期C++模板超編程已經有所出現。

**一般形式**
// The Curiously Recurring Template Pattern (CRTP)
template
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base
{
// ...
};


**靜態多態**
C++語言的多態，原本是用虛函數來實現的，屬於動態多態。安德烈·亞歷山德雷斯庫在Modern C++ Design中提出了奇異重現模板模式，並稱之為靜態多態（static polymorphism）。
template  
struct Base
{
void interface()
{
// ...
static_cast(this)->implementation();
// ...
}
static void static_func()
{
// ...
T::static_sub_func();
// ...
}
};
struct Derived : Base
{
void implementation();
static void static_sub_func();
};
基類模板利用了其成員函數體（即成員函數的實現）在聲明之後很久都不會被實例化（實際上只有被調用的模板類的成員函數才會被實例化），並利用了派生類的成員函數（通過類型轉化）。
在上例中，Base::interface()，雖然是在struct Derived之前就被聲明瞭，但未被編譯器實例化直至它被實際調用，這發生於Derived聲明之後，此時Derived::implementation()的聲明是已知的。
這種技術獲得了類似於虛函數的效果，並避免了動態多態的代價。也有人把CRTP稱為“模擬的動態綁定”。
這種模式廣泛用於Windows ATL與WTL庫，以及Boost.Iterator，Boost.Python或者Boost.Serialization等庫中。
考慮一個基類，沒有虛函數，則它的成員函數能夠調用的其它成員函數，只能是屬於該基類自身。當從這個基類派生其它類時，派生類繼承了所有未被覆蓋（overridden）的基類的數據成員與成員函數。如果派生類調用了一個被繼承的基類的函數，而該函數又調用了其它成員函數，這些成員函數不可能是派生類中的派生或者覆蓋的成員函數。也就是說，基類中是看不到派生類的。但是，基類如果使用了CRTP，則在編譯時派生類的覆蓋的函數可被選中調用。這效果相當於編譯時模擬了虛函數調用但避免了虛函數的尺寸與調用開銷（VTBL結構與方法查找、多繼承機制）等代價。但CRTP的缺點是不能在運行時做出動態綁定。
不通過虛函數機制，基類訪問派生類的私有或保護成員，需要把基類聲明為派生類的友元（friend）。如果一個類有多個基類都出現這種需求，聲明多個基類都是友元會很麻煩。一種解決技巧是在派生類之上再派生一個accessor類，顯然accessor類有權訪問派生類的保護函數；如果基類有權訪問accessor類，就可以間接調用派生類的保護成員了。這種方法被boost的多個庫使用，如：Boost.Python中的def_visitor_access和Boost.Iterator的iterator_core_access。原理示例代碼如下：
template class Base
{
private:
struct accessor : DerivedT  
{                                      // accessor類沒有數據成員，只有一些靜態成員函數
static int foo(DerivedT& derived)
{
int (DerivedT::*fn)() = &DeriveT::do_foo; //獲取DerivedT::do_foo的成員函數指針  
return (derived.*fn)();        // 通過成員函數指針的函數調用
}
};                                     // accessor類僅是Base類的成員類型，而沒有實例化為Base類的數據成員。
public:
DerivedT& derived()                    // 該成員函數返回派生類的實例的引用
{
return static_cast(*this);
}
int foo()
{                                       //  該函數具體實現了業務功能
return accessor::foo( this->derived());
}
};

struct Derived : Base             //  派生類不需要任何特別的友元聲明
protected:
int do_foo() 
{
// ... 具體實現 
return 1; 
}
};

**例子1：對象計數**
統計一個類的實例對象創建與析構的數據。可以輕鬆地利用CRTP實現:
template 
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  int counter::objects_created( 0 );
template  int counter::objects_alive( 0 );
class X : counter
{
// ...
};
class Y : counter
{
// ...
};

**例子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 
class Shape_CRTP : public Shape {
public:
virtual Shape *clone() const {
return new Derived(static_cast(*this));
}
};
// Nice macro which ensures correct CRTP usage
#define Derive_Shape_CRTP(Type) class Type: public Shape_CRTP
// 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 shapePtr->clone().

**例子3：不可派生的類== 
一個類如果不希望被繼承，類似於Java中的具有finally性質的類，這在C++中可以用虛繼承來實現：
template class MakeFinally{
private:
MakeFinally(){}//只有MakeFinally的友類才可以構造MakeFinally
~MakeFinally(){}
friend 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++標準庫頭文件中，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對象。
class mySharedClass:public  std::enable_shared_from_this{
public:
// ...
};
int main()
{
std::vector> spv;
spv.push_back(new mySharedClass());
std::shared_ptr p(new mySharedClass());
mySharedClass &c=*p;
spv.emplace_back(c.shared_from_this());
}

**其它語言**
在Java與.NET Framework中，常見把一個類作為泛型超類或interface的類型參數。例如，下述Java類實現了標準庫中的泛型界面Comparable:
public class Item implements Comparable {
	private String name;
	
	@Override
	public int compareTo(final Item other) {
		return name.compareTo(other.name);
	}
}
這保證了編譯時不會比較Item與其它不是Item的對象如String。與C++不同，Java類不能使用不同的類型參數擴展一個超類兩次，也不能用不同的類型參數實現同一個interface兩次。這是由於Java實用了類型擦除處理泛型。例如，上例的class Item不能同時實現Comparable與Comparable。詳見Problems with type erasure。

**參見**
 Barton–Nackman trick- F-受限量化

**參考文獻==