C++:18---函式模板(template)
一、模板的定義
template<typename T>
以關鍵字template開頭,後面跟一個模板引數列表,列表裡面用逗號將多個模板引數隔開定義的注意事項
模板的編譯
當編譯器遇到一個模板定義時,並不生成程式碼。只有當例項化處模板的一個特定版本時,編譯器才會生成程式碼
重點:通常,當我們呼叫一個函式/定義例項化一個類時,編譯器只需掌握函式的宣告/類的宣告即可,因此可以把函式/類的宣告放置在標頭檔案,而把函式/類的定義放置在原始檔中。但是模板則不同:為了例項化模板函式,編譯器必須掌握函式模板/類模板成員函式的宣告和定義,因此只能將模板函式/類的宣告和定義都放置在頭一個頭檔案/原始檔中(重點)
二、函式模板與模板函式
函式模板:一個模板(是模板)
模板函式:呼叫函式模板時生成的函式(是函式),也稱為函式模板的例項化
一個模板引數列表只和一個函式模板相對應。因此每定義一個函式模板就需要重新定義一個模板引數列表
//定義模板以及一個函式模板
template<typenameT>intcompare(constT&v1,constT&v2);int main(){compare(1,2);//模板函式,也稱為模板的例項化compare("ABC","DEF");//模板函式return0;}
三、模板型別引數
模板引數列表不能為空
模板引數既可以用typename宣告,也可以使用class宣告。不過建議前者
//定義模板以及一個函式模板
template<typenameT,classU>intcompare(constT&v1,constU&v2);int main(){compare(1,1);//T為int,U也為intcompare(1,"DEF");//T為int,U也為stringreturn 0;
}
四、非型別模板引數
除了定義上面的模板型別引數,我們也可以定義非型別引數。
一個非型別引數表示一個值而非一個型別。並且通過特定的型別名定義而非typename或class來定義
當一個模板被例項化時,非型別引數被使用者提供或者編譯器推斷的值所代替。
重點:一個非型別引數可以是一個整型、一個指向物件或函式的指標(或引用)。且實參必須是一個常量表達式
//定義模板以及一個函式模板。
template <unsigned N, unsigned M>
int compare(const char(&p1)[N], const char(&p2)[M])//傳入陣列的引用來比較陣列大小
{
return strcmp(p1, p2);
}
int main()
{
compare("hi","mom");//編譯器會使用字面值常量的大小來代替N和M
return 0;
}
五、inline、constexpr函式模板
函式模板可以宣告為inline或constexpr。但是這些關鍵字必須放在函式的返回值型別前面,模板引數列表的後面
template<typename T> //正確
inline T func(T const&);
constexpr template<typename T> //錯誤,constexpr位置錯誤
T func2(T const&);
六、定義型別無關的程式碼
當我們定義函式模板時,如果函式能處理的功能只限於一些特定的情況,而不能作用於大多數的情況,那麼這個函式模板的操作性與可移植性就比較差
為了解決上面這個問題,我們在定義函式模板時,就需要考慮型別無關與可移植性
案例:
下面這個函式模板如果呼叫它比較兩個指標,而這兩個指標未指向相同的陣列,則程式碼的行為是未定義的
template <typename T> int compare(const T& s1, const T& s2) { if (v1 < v2) return -1; if (v1 > v2) return 1; return 0; }
下面我們編寫了這個函式模板,也可以用於傳入指標也可以正常使用的函式模板(但是還不是最完美的,所以在定義時,要考慮各種因素而達到更高的標準)
template <typename T> int compare(const T& s1, const T& s2) { if (less<T>()(v1,v2)) return -1; if (less<T>()(v2, v1)) return 1; return 0; }
前方高能,我來出一個程式碼例子,來看看結果和你想的是否一致?
練習:
#include <iostream>
#include <cstring>
using namespace std;
template<typename T1,typename T2>
bool Compare(const T1& t1,const T2& t2)
{
cout << "call Compare(const T1& t1,const T2& t2)" << endl;
if(t1 > t2){
return true;
}
return false;
}
bool Compare(const char* s1,const char* s2)
{
cout << "call Compare(const char* s1,const char* s2)" << endl;
return strcmp(s1,s2);
}
bool Compare(const float& t1,const float& t2)
{
cout << "call Compare(const float& t1,const float& t2)" << endl;
if(t1 > t2){
return true;
}
return false;
}
int main(){
int a = 12,b=23;
Compare(a,b);
const char *p = "11111";
const char *q = "00";
Compare(p,q);
float c=12.0,d=15.7;
Compare(c,d);
return 0;
}
為什麼結果是
call Compare(const T1& t1,const T2& t2)
call Compare(const char* s1,const char* s2)
call Compare(const float& t1,const float& t2)
呢?接下來就是我們下一節要講的模板的特化。