我們知道C++是一種“強類型”語言。也就是說,對於一個變量,編譯器必須確切知道它是什麼類型。但是,這種強類型函數在實現一些簡單函數反而更麻煩。例如:求兩大數的較大者,應以Max( )函數,我們需要對不同數據類型分別定義不同重載版本來實現:
int Max(int x,int y) //比較兩個int類型的值
{
return ((x > y) ? x : y);
}
float Max(float x,float y) //比較兩個float類型的值
{
return ((x > y) ? x : y);
}
double Max(double x,double y) //比較兩個double類型的值
{
return ((x > y) ? x : y);
}
我們看到,雖然我們可以通過函數重載去實現,但明顯存在一些缺點:
1、所有的函數除返回類型外,函數體都相同,代碼複用率低;
2、只要新類型出現,我們就需要添加新的對應函數;
3、維護不方便。
這時你可能會想到c語言學到的預處理程序,通過以下方式來實現:
#define Max(x,y)((x > y) ? x : y)
但是同樣,我們都知道這不是函數,不會進行函數檢測,安全性太低。
那麼,這時我們就想,能不能只寫一套代碼,對於任意類型T的兩個對象x、y,函數調用Max(x,y)總能使編譯系統理解其比較意義而實現我們編程的目的?
爲了解決這個問題,C++引用了模板機制。
什麼是模板?
C++程序有類和函數組成,模板分爲類模板和函數模板。模板就是把功能相似,僅數據類型不同的函數或類設計爲通用的函數模板或類模板,提供給用戶。
模板是“泛型編程”的基礎。簡單的說,類是對象的抽象,而模板是類的抽象,用模板能定義具體類。
函數模板的一般定義形式:
template<typename Paraml,typename Paraml,... ,class Paraml>
返回類型 函數名(函數形參表)
{
}
模板定義以關鍵字template開始,形參由關鍵字class或typename及其後面的類型名構成,一般建議儘量使用typename。
注意:不能使用struct代替typename。
模板函數也可以定義爲inline函數
template<typename T>
inline T Add(const T _left, const T _right)
{
return (_le _right);
}
注意:inline關鍵字必須放在模板形參表之後,返回值之前,不能放在template之前
例如:前面的Max()函數可以用模板定義如下:
template<typename T>
T Max(T x,T y)
{
return ((x > y) ? x : y);
}
模板是一個藍圖,它本身不是類也不是函數,編譯器用模板產生的指定的類或者函數的特定類型版本,產生模板特定類型的過程稱爲函數模板實例化。
實例:定義一個函數模板,比較兩個數大小。
#include<iostream>
using namespace std;
template<typename T>
T Max(T x, T y)
{
return ((x > y) ? x : y);
}
int main()
{
cout << Max(10, 20) << endl;
cout << Max(13.14, 5.2) << endl;
cout << Max(10, (int)5.2) << endl;
cout << Max<int>(10, 5.2) << endl;
}
模板實例化過程:
具體如下
模板參數
類型參數可以用來指定返回類型或函數的參數類型,以及在函數體內用於變量的聲明或類型的轉換:
template<typename T>
T foo(T* p)
{
T tmp = *p;
return tmp;
}
模板形參的名字在同一模板形參列表中只能使用一次
template <typename T,typename T>
void fun(T t1, T t2)
{
}
所有模板形參前面必須加上class或者typename關鍵字修飾
template <typename T,U>
void fun(T t, U u)
{
}
非類型模板參數
一個非類型參數表示一個值而非一個類型。非模板類型形參是模板內部定義的常量,在需要常量表達式的時候,可以使用非模板類型參數。
例如數組長度:
說明:
1、一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例
化爲這個非模板函數。
2、對於非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調動非模板
函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數,
那麼將選擇模板。
3、顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個調用,
而且所有的模板參數都應該根據實參演繹出來。
4、模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換。
模板函數特化
特化的一般形式:
1、關鍵字template後面接一對空的尖括號<>
2、函數名後接模板名和一對尖括號,尖括號中指定這個特化定義的模板形參
3、函數形參表
4、函數體
template<>
返回值 函數名<Type>(參數列表)
{
// 函數體
}
例如,對於前面的Max()函數模板。
template<class T>
T Max(T x,T y)
{
return ((x>y) ? x:y);
}
如果比較的是兩個“const char*”類型,那麼函數模板用“const char*”型的模板實參實例化。如果還想讓每個實參都被解釋爲C風格的字符串而不是字符指針,那麼餓通過模板定義給出的語義就不正確了,必須爲函數模板實例化提供“const char*”的特化:
#include<cstring> //引入cstring的相關聲明
template<> //特化標誌
const char* Max<const char*>(const char* x,const char* y) //用const char*轉化
{
return (strcmp(x,y)<0)?x:y;
}
特化的聲明必須與特定的模板相匹配,否則:
template<>
int compare<int>(int);
由於有了這個特化,程序中對所有用兩個“const char*”型形參進行調用的Max()都會調用這個特化的定義,而對於其他的調用,則通過模板定義實例化對象:
const char* a = “hello”;
const char* b = "world";
int x = 10;
int y = 20;
Max(a,b); //調用模板的特化版本進行實例化
Max(x,y); //調用模板的通用版本進行實例化
注意:在模板特化版本的調用中,實參類型必須與特化版本函數的形參類型完全匹配,如果不匹配,編譯器將爲實參模板定義中實例化一個實例。
注意:特化不能出現在模板實例的調用之後,應該在頭文件中包含模板特化的聲明,然後使用該特化版本的每個源文件包含該頭文件。。