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