資料型別給程式設計帶來的困擾及解決方案
int maxt(int, int);
double maxt(double, double);
若有一種佔位符T
,能夠代替型別,便可以簡化程式碼的冗餘編寫
T maxt(T,T);
C++模板
模板宣告如下
template<typename T1, ...>
template
是C++的模板宣告關鍵字,尖括號內為模板引數列表
typename
為型別佔位符宣告關鍵字
template<typename T>
T maxt(T x, T y){
return (x>y)? x: y;
}
函式模板
在預編譯階段,當程式中呼叫函式模板時,編譯器會用實際型別替換型別佔位符生成實體函式
若編譯器可以從函式實參中推匯出模板引數所需型別,則可以不傳入模板引數
template<typename T>
T maxt(T x, T y){
return (x>y)?x:y;
}
int main(int argc, char* argv[]){
// std::cout<< maxt<int>(4,6)<< std::endl;
std::cout<< maxt(4,6)<< std::endl;
return 0;
}
類别範本
在宣告類時,使用template
進行模板宣告即可
template<typename T>
class Circle{
public:
Circle(T r);
}
若在類别範本外實現成員函式,則必須宣告為函式模板
template<typename T>
Circle<T>::Circle(T r){}
在呼叫時,需要在類名後使用尖括號傳遞具體型別
Circle<int> circle;
STL的模板程式設計對物件導向技術並不感興趣,其認為類對資料的過度封裝影響程式的執行效率
而為了更好的管理程式碼,所以STL中使用大量沒有訪問許可權的struct
製作的類别範本
變數模板
變數模板,將模板擴充套件到變數
如pi<T>
的實現
當T
為double
時,返回3.14
當T
為int
時,返回3
當T
為string
時,返回"3.14"
或"pi"
C++新標準對泛型設計的努力
auto和decltype
C++11中,auto
關鍵字,用來推導變數的資料型別auto a=100;
auto
型別的獲取可透過編譯器的型別記憶能力或decltype
的型別提示來推導
利用型別記憶推導複雜型別
auto
目前能力有限,只對系統的內建資料型別有效
對於使用者自定義型別或複雜型別,只有當編譯器取得足夠經驗後,才具備推導能力
map<int,map<int,int>>::const_iterator iter1=map1.begin();
auto iter2=map1.begin();
由於前一條語句告知了編譯器map1.begin()
的型別,在處理第二條語句時,便利用了記憶能力自動推匯出iter2
的型別
decltype
表示式對推導函式返回值型別進行指導
變數型別難以確定的問題一般出現在函式返回值上,C++11可以使用decltype
對函式返回值的型別推導工作進行指導
當返回auto
型別,需要編譯器對函式返回值型別進行推導時,可用decltype
對該推導工作進行指導
template<typename T, typename U>
auto Multiply(T t, U u)->decltype(t*u){
return t*u;
}
這種使用auto
作為函式返回值型別的稱為auto返回值佔位
將auto
看作資料型別,則auto
也是一種泛型,只不過無須關鍵字typename
宣告
且實際型別不是由實參顯式提供,而是根據型別操作相關歷史記憶及應用程式提供的推導思路
模板引數
根據引數實參的性質,模板引數分為型別引數,非型別引數和模板定義型引數三種
型別引數
用關鍵字typename
宣告的引數
型別引數的型別實參包括:
- 系統內建的型別
- 使用者自定義的資料型別
- 編譯器剛學到的類别範本實體
- 由
typename
定義的型別別名
非型別引數
C++允許在模板引數列表中定義普通變數或物件,如template<typename T, int a>
由於模板引數是在預編譯階段進行傳遞並被編譯的,故這種非型別引數在模板程式碼內是常量,不能修改
對於這種引數,目前C++僅支援整型int
(或可轉為int
的型別,如bool
),列舉,指標和引用型別
C++11支援非型別引數在定義時賦值,如template<typename T, int b=100>
模板定義型引數
以類别範本作為類别範本引數,除了強調這個型別引數必須為類别範本外,還強調該類别範本的引數個數
// 單模板引數的類别範本
template<typename T>
struct S_Tmp{};
// 多模板引數的類别範本
template<typename T, typename R>
struct D_Tmp{};
// 以單引數類别範本作為引數的類别範本
template<template<typename S>class T>
struct MyTest{};
int main(){
MyTest<S_Tmp> tt1;
// MyTest<D_Tmp> tt1; // error
return 0;
}
模板形參和實參的結合
函式模板實參的隱式結合
編譯器可以根據函式實參型別推匯出模板形參所對應的實參,這種在呼叫函式模板時可以省略模板引數列表
由於函式呼叫語句中不提供函式返回值的型別資訊,所以模板的返回值型別佔位符必須與某個形參的佔位符相同
指標實參
C++中,指標是一種資料型別,因此可作為模板實參
修飾字const和&的使用
可以在模板呼叫引數列表中使用修飾字const
和&
template<typename T1, typename T2>
const T1& add(const T1& a, const T2& b){
return a;
}
模板特例化與模板具現
模板特例化
資料型別的變化通常與業務邏輯無關
若有個別資料型別所對應的演算法與其他型別對應的演算法不同,這類演算法就要單獨編寫
函式模板的特化
如判斷大小的函式,數值型別與字串型別的比較演算法是不一樣的,應該分開實現
template<typename T>
T mymax(T a, T b){
return a>b?a:b;
}
template<>
char* mymax(char* a, char* b){
return (strcmp(a,b)<0)?b:a;
}
使用template<>
是為了將其納入maxt
模板體系
類别範本的特化與偏特化
// 普通模板
template<typename T1, typename T2>
struct Test{};
// 偏特化模板
template<typename T>
struct Test<int, T>{};
// 全特化模板
template<>
struct Test<int, float>{};
模板的具現
編譯器在匹配模板生成實體程式碼時的優先順序
- 特化模板(函式或類)
- 偏特化模板(類)
- 普通模板(函式或類)