【摘 ?要】結(jié)合實(shí)例介紹了C++11中模板的語(yǔ)法規(guī)則,包括函數(shù)模板、類模板以及函數(shù)和類模板的特化,隨后對(duì)模板在元編程領(lǐng)域進(jìn)行了討論,并總結(jié)了元編程的優(yōu)缺點(diǎn)。
【關(guān)鍵詞】C++;元編程;程序設(shè)計(jì);
引言
C++模板是支持參數(shù)化多態(tài)的工具,標(biāo)準(zhǔn)庫(kù)中如std::vector,std::list等都是模板,可以支持多種類型,用來(lái)實(shí)現(xiàn)代碼的復(fù)用。C++中模板分為兩類:函數(shù)(function)模板和類(class)模板[1]。模板元編程是這兩種模板演變而來(lái)的一項(xiàng)較為高級(jí)的編程技巧,Alexandrescu在2001年發(fā)表的《Modern C++ Design》[2]及模板程序庫(kù)Loki[3]間接地導(dǎo)致了模板元編程庫(kù)的出現(xiàn),書中所使用泛型組件等方法令人眼前一亮,由此模板元編程進(jìn)入人們的視線。本文先介紹了函數(shù)模板和類模板,然后討論了模板元編程。
函數(shù)模板
函數(shù)模板可以定義一類的函數(shù),這樣可以讓重復(fù)的操作通過(guò)抽象聚集到一個(gè)函數(shù),減少代碼量。函數(shù)模板的語(yǔ)法如下:
template <模板參數(shù)列表> 函數(shù)聲明
其中模板參數(shù)列表可以是類型,也可以是整型參數(shù),而函數(shù)聲明跟一般的函數(shù)聲明一致,不過(guò)函數(shù)的返回值和參數(shù)列表的類型可以是模板參數(shù)列表中聲明的類型。下面是一個(gè)相同類型相加的函數(shù)Add:
template <typename T>
auto Add(T a,T b) -> decltype(a + b){
return a + b;
}
對(duì)于這樣的實(shí)現(xiàn),我們可以這樣調(diào)用:
std::cout << Add(10,10)<< std::endl;
對(duì)于函數(shù)模板,編譯器會(huì)自動(dòng)擴(kuò)展已經(jīng)實(shí)列化的模板。上述例子,編譯器會(huì)自動(dòng)生int Add(int a,int b)函數(shù)。
上述例子表明:函數(shù)模板是一系列函數(shù)的抽象,形參可以是允許的任意類型,使用相同函數(shù)模板可以實(shí)現(xiàn)不同數(shù)據(jù)類型的相同運(yùn)算操作。
類模板
類模板的語(yǔ)法如下:
template <模板參數(shù)列表> 類的聲明
模板參數(shù)列表與函數(shù)模板相同,類的成員以及類的成員函數(shù)可以使用模板參數(shù)列表聲明的類型和常量,類的成員函數(shù)也可以是函數(shù)模板。我們可以借助類模板來(lái)實(shí)現(xiàn)單例設(shè)計(jì)模式,代碼如下:
template <class T>
class Singleton {
protected:
Singleton(){}
~Singleton(){}
public:
static T& Instance(){
static T gInstance;
return gInstance;
}
};
使用時(shí)只需要繼承這個(gè)類,就不需要重復(fù)實(shí)現(xiàn)Instance方法了,有效地復(fù)用了代碼。
模板元編程
基于函數(shù)模板和類模板,人們提出了模板元編程的編程方法。模版元編程完全不同于一般C++的運(yùn)行期程序,它非常獨(dú)特。這是因?yàn)槟0嬖绦虻膱?zhí)行時(shí)期是在編譯期,且模版元程序操縱的數(shù)據(jù)不能是運(yùn)行時(shí)變量,只能是編譯期不可修改的常量,外加它可以用到的語(yǔ)法元素相當(dāng)有限,無(wú)法使用運(yùn)行期的某些語(yǔ)法,比如if-else邏輯分支,for循環(huán)語(yǔ)句等都不能用來(lái)進(jìn)行模板元編程。因此,模版元編程需要多項(xiàng)技巧來(lái)配合,導(dǎo)致編寫模版元編程比較復(fù)雜也比較困難。下面我們使用模板元編程來(lái)實(shí)現(xiàn)整數(shù)的求和:
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;
};
上述例子通過(guò)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;
會(huì)出現(xiàn)編譯錯(cuò)誤。因?yàn)槟0迨蔷幾g時(shí)期確定的,不能像函數(shù)一樣可以傳入變量,它只能是常量??梢园l(fā)現(xiàn):模板元編程只操作在編譯期可以確定的量,編程思維跟普通C++程序開發(fā)有很大的區(qū)別,而且語(yǔ)法較為繁瑣;由于元編程在大數(shù)情況下,用到了遞歸,而元編程又是在編譯期確定的,這樣會(huì)拉長(zhǎng)程序的編譯時(shí)間;在C++11中,編譯器對(duì)模板的錯(cuò)誤提示并不友好,這樣會(huì)導(dǎo)致元編程在調(diào)試方面有極大的障礙。但是模板元編程得到的計(jì)算可以說(shuō)是一個(gè)常量,理論上無(wú)論其復(fù)雜度多大,在運(yùn)行時(shí)去訪問(wèn),其復(fù)雜度為O(1),同時(shí)也能在一定程度上減少程序發(fā)布大小。
總結(jié)
因?yàn)槟0迨欠夯艘幌盗蓄愋偷南嗤僮?,這樣比不用模板,更能有效的復(fù)用代碼,減少代碼量。而在C++11中,模板元編程在編譯期就能確定計(jì)算結(jié)果,但會(huì)增加編譯時(shí)間,以及調(diào)試?yán)щy等缺點(diǎn),在應(yīng)用模板編程時(shí),需要全面權(quán)衡。
參考文獻(xiàn):
[1] 趙娟.模板在C++中的應(yīng)用[J].電腦知識(shí)與技術(shù),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/
作者簡(jiǎn)介:
方言(1995年-),湖南人,寧夏大學(xué)碩士研究生在讀,主要研究物聯(lián)網(wǎng)技術(shù)方向。
(作者單位:寧夏大學(xué)信息工程學(xué)院)