函數模闆
函數模闆是那些被參數化的函數,它們代表的是一個函數家族。
初探函數模闆
函數模闆提供了一種函數行為,該函數行為可以用多種不同的類型進行調用;也就是說,函數模闆代表一個函數家族。它的表示(即外形)看起來和普通的函數很相似,唯一的差別是函數元素是未确定的:這些元素将在使用時被參數化。
定義模闆
下面是一個傳回兩個值中最大者的函數模闆:
使用模闆
下面的程式展示了如何使用max()函數模闆:
在這個程式中,max()被調用了3次,調用實參每次都不相同:一次用兩個int,一個用兩個double,一次用兩個string。
可以看到:max()模闆每次調用的前面都有域限定符::,這是為了确認我們調用的是全局名字空間中的max()。因為标準庫也有一個std::max()模闆,在某些情況下也可以被使用,是以有時還會産生二義性。
通常而言,并不是把模闆編譯成一個可以處理任何類型的單一實體;而是對于執行個體化模闆參數的每種類型,都從模闆産生出一個不同的實體。是以,針對3種類型中的每一種,max()都被編譯了一次。例如,max()的第一次調用:
int i=42;
max(7,i);
使用了以int為模闆參數T的函數模闆。是以,它具有調用如下代碼的語義:
這種用具體類型代替模闆參數的過程叫做執行個體化。它産生了一個模闆的執行個體。
可以看到:隻要使用函數模闆,(編譯器)會自動地引發這樣一個執行個體化過程,是以程式員并不需要額外地請求模闆的執行個體化。
如果試圖機遇與一個不支援模闆内部所使用操作的類型執行個體化一個模闆,那麼将會導緻一個編譯器錯誤,例如:
std::complex<float> c1,c2;//std::complex并不支援operator<
max(c1,c2); //編譯器錯誤
于是,我們可以得出一個結論:模闆被編譯了兩次,分别發生在
1 執行個體化之前,先檢查模闆代碼本身,檢視文法是否正确;在這裡會發現錯誤的文法,如遺漏分号等。
2 在執行個體化期間,檢查模闆代碼,檢視是否所有的調用都有效。在這裡會發現無效的調用,如該執行個體化類型不支援某些函數調用等。
注意:使用函數模闆,并且引發模闆執行個體化的時候,編譯器(在某時刻)需要檢視模闆的定義。這就不同于普通函數中編譯和連接配接之間的差別。因為對普通函數而言,隻要有該函數的聲明(即不需要定義),就可以順利通過編譯。
實參的演繹
當我們為某些實參調用一個諸如max()的模闆時,模闆參數可以由我們所傳遞的實參來決定。如果我們傳遞了兩個int給參數類型T const&,那麼C++編譯器能夠得出結論:T必須是int。注意,這裡不允許進行自動類型轉換,每個T都必須正确地比對。例如,
template <typename T>
inline T const& max(T const& a,T const& b)
max(4,7); //OK:兩個實參的類型是int
max(4,4.2); //ERROR:第一個T是int,而第二個是double
有3種方法可以用來處理上面這個錯誤:
1 對實參進行強制類型轉換,使他們可以互相比對:
max(static_cast<double>(4),4.2); //OK
2顯示指定(或者限定)T的類型:
max<double>(4,4.2);
3 指定兩個參數可以具有不同的類型。