【摘 ?要】結合實例介紹了C++11中模板的語法規則,包括函數模板、類模板以及函數和類模板的特化,隨后對模板在元編程領域進行了討論,并總結了元編程的優缺點。
【關鍵詞】C++;元編程;程序設計;
引言
C++模板是支持參數化多態的工具,標準庫中如std::vector,std::list等都是模板,可以支持多種類型,用來實現代碼的復用。C++中模板分為兩類:函數(function)模板和類(class)模板[1]。模板元編程是這兩種模板演變而來的一項較為高級的編程技巧,Alexandrescu在2001年發表的《Modern C++ Design》[2]及模板程序庫Loki[3]間接地導致了模板元編程庫的出現,書中所使用泛型組件等方法令人眼前一亮,由此模板元編程進入人們的視線。本文先介紹了函數模板和類模板,然后討論了模板元編程。
函數模板
函數模板可以定義一類的函數,這樣可以讓重復的操作通過抽象聚集到一個函數,減少代碼量。函數模板的語法如下:
template <模板參數列表> 函數聲明
其中模板參數列表可以是類型,也可以是整型參數,而函數聲明跟一般的函數聲明一致,不過函數的返回值和參數列表的類型可以是模板參數列表中聲明的類型。下面是一個相同類型相加的函數Add:
template <typename T>
auto Add(T a,T b) -> decltype(a + b){
return a + b;
}
對于這樣的實現,我們可以這樣調用:
std::cout << Add(10,10)<< std::endl;
對于函數模板,編譯器會自動擴展已經實列化的模板。上述例子,編譯器會自動生int Add(int a,int b)函數。
上述例子表明:函數模板是一系列函數的抽象,形參可以是允許的任意類型,使用相同函數模板可以實現不同數據類型的相同運算操作。
類模板
類模板的語法如下:
template <模板參數列表> 類的聲明
模板參數列表與函數模板相同,類的成員以及類的成員函數可以使用模板參數列表聲明的類型和常量,類的成員函數也可以是函數模板。我們可以借助類模板來實現單例設計模式,代碼如下:
template <class T>
class Singleton {
protected:
Singleton(){}
~Singleton(){}
public:
static T& Instance(){
static T gInstance;
return gInstance;
}
};
使用時只需要繼承這個類,就不需要重復實現Instance方法了,有效地復用了代碼。
模板元編程
基于函數模板和類模板,人們提出了模板元編程的編程方法。模版元編程完全不同于一般C++的運行期程序,它非常獨特。這是因為模版元程序的執行時期是在編譯期,且模版元程序操縱的數據不能是運行時變量,只能是編譯期不可修改的常量,外加它可以用到的語法元素相當有限,無法使用運行期的某些語法,比如if-else邏輯分支,for循環語句等都不能用來進行模板元編程。因此,模版元編程需要多項技巧來配合,導致編寫模版元編程比較復雜也比較困難。下面我們使用模板元編程來實現整數的求和:
template <int...> struct Sum;
template <int A> struct Sum<A> {
static constexpr int value = A;
};
template <int A,int B> struct Sum<A,B> {
static constexpr int value = A + B;
};
template <int A,int B,int...Args> struct Sum<A,B,Args...> {
static constexpr int value = Sum<A,B>::value + Sum<Args...>::value;
};
上述例子通過VS2019的編譯。使用方法如下:
std::cout << Sum<1,2>()<< std::endl;// print 3
std::cout << Sum<1,2,3,4,5>()<< std::endl;// print 15
但是這樣使用:
int a = 1,b = 2;
std::cout << Sum<a,b>()<< std::endl;
會出現編譯錯誤。因為模板是編譯時期確定的,不能像函數一樣可以傳入變量,它只能是常量。可以發現:模板元編程只操作在編譯期可以確定的量,編程思維跟普通C++程序開發有很大的區別,而且語法較為繁瑣;由于元編程在大數情況下,用到了遞歸,而元編程又是在編譯期確定的,這樣會拉長程序的編譯時間;在C++11中,編譯器對模板的錯誤提示并不友好,這樣會導致元編程在調試方面有極大的障礙。但是模板元編程得到的計算可以說是一個常量,理論上無論其復雜度多大,在運行時去訪問,其復雜度為O(1),同時也能在一定程度上減少程序發布大小。
總結
因為模板是泛化了一系列類型的相同操作,這樣比不用模板,更能有效的復用代碼,減少代碼量。而在C++11中,模板元編程在編譯期就能確定計算結果,但會增加編譯時間,以及調試困難等缺點,在應用模板編程時,需要全面權衡。
參考文獻:
[1] 趙娟.模板在C++中的應用[J].電腦知識與技術,2019(20).
[2] Andrei Alexandrescu.Modern C++ Design[M].US:Addison-Wesley Professional,2002
[3] Andrei Alexandrescu.”Loki Library” [OL]http://loki-lib.sourceforge.net/
作者簡介:
方言(1995年-),湖南人,寧夏大學碩士研究生在讀,主要研究物聯網技術方向。
(作者單位:寧夏大學信息工程學院)