【C++】淺談C++模板

我們知道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);      //調用模板的通用版本進行實例化  

注意:在模板特化版本的調用中,實參類型必須與特化版本函數的形參類型完全匹配,如果不匹配,編譯器將爲實參模板定義中實例化一個實例。
這裏寫圖片描述
注意:特化不能出現在模板實例的調用之後,應該在頭文件中包含模板特化的聲明,然後使用該特化版本的每個源文件包含該頭文件。。

發佈了49 篇原創文章 · 獲贊 28 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章