最近一直在看STL和Boost,源碼里邊好多涉及到模板元編程技術(shù),簡單了解一下,備忘(Boost Python中的涉及模板元的部分重點關(guān)注一下)。
范例引入
// 主模板
template<int N>
struct Fib
{
enum { Result = Fib<N-1>::Result + Fib<N-2>::Result };
};
// 完全特化版
template <>
struct Fib<1>
{
enum { Result = 1 };
};
// 完全特化版
template <>
struct Fib<0>
{
enum { Result = 0 };
};
int main()
{
int i = Fib<10>::Result;
// std::cout << i << std::endl;
}
主要思想
利用模板特化機制實現(xiàn)編譯期條件選擇結(jié)構(gòu),利用遞歸模板實現(xiàn)編譯期循環(huán)結(jié)構(gòu),模板元程序則由編譯器在編譯期解釋執(zhí)行。
優(yōu)劣及適用情況
通過將計算從運行期轉(zhuǎn)移至編譯期,在結(jié)果程序啟動之前做盡可能多的工作,最終獲得速度更快的程序。也就是說模板元編程的優(yōu)勢在于:
1.以編譯耗時為代價換來卓越的運行期性能(一般用于為性能要求嚴格的數(shù)值計算換取更高的性能)。通常來說,一個有意義的程序的運行次數(shù)(或服役時間)總是遠遠超過編譯次數(shù)(或編譯時間)。
2.提供編譯期類型計算,通常這才是模板元編程大放異彩的地方。
模板元編程技術(shù)并非都是優(yōu)點:
1.代碼可讀性差,以類模板的方式描述算法也許有點抽象。
2.調(diào)試困難,元程序執(zhí)行于編譯期,沒有用于單步跟蹤元程序執(zhí)行的調(diào)試器(用于設(shè)置斷點、察看數(shù)據(jù)等)。程序員可做的只能是等待編譯過程失敗,然后人工破譯編譯器傾瀉到屏幕上的錯誤信息。
3.編譯時間長,通常帶有模板元程序的程序生成的代碼尺寸要比普通程序的大,
4.可移植性較差,對于模板元編程使用的高級模板特性,不同的編譯器的支持度不同。
總結(jié):
模板元編程技術(shù)不適用普通程序員的日常應(yīng)用,它常常會做為類庫開發(fā)的提供技術(shù)支持,為常規(guī)模板代碼的內(nèi)核的關(guān)鍵算法實現(xiàn)更好的性能或者編譯期類型計算。模板元程序幾乎總是應(yīng)該與常規(guī)代碼結(jié)合使用被封裝在一個程序庫的內(nèi)部。對于庫的用戶來說,它應(yīng)該是透明的。
工程應(yīng)用實例
1. Blitz++:由于模板元編程最先是因為數(shù)值計算而被發(fā)現(xiàn)的,因此早期的研究工作主要集中于數(shù)值計算方面,Blitz++庫利用模板將運行期計算轉(zhuǎn)移至編譯期的庫,主要提供了對向量、矩陣等進行處理的線性代數(shù)計算。
2.Loki:將模板元編程在類型計算方面的威力應(yīng)用于設(shè)計模式領(lǐng)域,利用元編程(以及其他一些重要的設(shè)計技術(shù))實現(xiàn)了一些常見的設(shè)計模式之泛型版本。Loki庫中的Abstract Factory泛型模式即借助于這種機制實現(xiàn)在不損失類型安全性的前提下降低對類型的靜態(tài)依賴性。
3.Boost:元編程庫目前主要包含MPL、Type Traits和Static Assert等庫。 Static Assert和Type Traits用作MPL的基礎(chǔ)。Boost Type Traits庫包含一系列traits類,用于萃取C++類型特征。另外還包含了一些轉(zhuǎn)換traits(例如移除一個類型的const修飾符等)。Boost Static Assert庫用于編譯期斷言,如果評估的表達式編譯時計算結(jié)果為true,則代碼可以通過編譯,否則編譯報錯。
技術(shù)細節(jié)
模板元編程使用靜態(tài)C++語言成分,編程風(fēng)格類似于函數(shù)式編程,在模板元編程中,主要操作整型(包括布爾類型、字符類型、整數(shù)類型)常量和類型,不可以使用變量、賦值語句和迭代結(jié)構(gòu)等。被操縱的實體也稱為元數(shù)據(jù)(Metadata),所有元數(shù)據(jù)均可作為模板參數(shù)。
由于在模板元編程中不可以使用變量,我們只能使用typedef名字和整型常量。它們分別采用一個類型和整數(shù)值進行初始化,之后不能再賦予新的類型或數(shù)值。如果需要新的類型或數(shù)值,必須引入新的typedef名字或常量。
其它范例
// 僅聲明
struct Nil;
// 主模板
template <typename T>
struct IsPointer
{
enum { Result = false };
typedef Nil ValueType;
};
// 局部特化
template <typename T>
struct IsPointer<T*>
{
enum { Result = true };
typedef T ValueType;
};
// 示例
int main()
{
cout << IsPointer<int*>::Result << endl;
cout << IsPointer<int>::Result << endl;
IsPointer<int*>::ValueType i = 1;
//IsPointer<int>::ValueType j = 1;
// 錯誤:使用未定義的類型Nil
}
//主模板
template<bool>
struct StaticAssert;
// 完全特化
template<>
struct StaticAssert<true>
{};
// 輔助宏
#define STATIC_ASSERT(exp)\
{ StaticAssert<((exp) != 0)> StaticAssertFailed; }
int main()
{
STATIC_ASSERT(0>1);
}
References:
http://club.topsage.com/thread-421469-1-1.html
http://wenku.baidu.com/view/c769720df78a6529647d539d.html
Blitz++: http://www.oonumerics.org/blitz .
Loki :http://sourceforge.net/projects/loki-lib
Boost:http://www.boost.org/