1、引子ios
考慮求兩數較大值函數max(a,b)
對於a,b的不一樣類型,都有相同的處理形式:
c++
return a < b ? b : a;編程
用已有方法解決:
(1)宏替換 #define max(a,b) ((a)< (b) ? (b) : (a))
函數
存在的問題:避開類型檢查工具
(2)重載
spa
存在的問題:須要許多重載版本設計
(3)使用函數模板
指針
2、模板對象
模板是一種參數化的多態工具
所謂參數化的多態性,是指將程序所處理 的對象的類型參數化,使一段程序代碼能夠用於處理多不一樣類型的對象。
採用模板編程,能夠爲各類邏輯功能相同而數據類型不一樣的程序提供一種代碼共享的機制。
ci
模板包括函數模板(function template)、類模板(class template)。本文主要討論函數模板
3、函數模板
(一)、函數模板的使用
函數模板的通常說明形式以下:
template < 模板形參表>
返回值類型 函數名(模板函數形參表){
//函數定義體
}
一、函數模板的定義以關鍵字template開頭
二、template以後<>中是函數模板的參數列表
三、函數模板的參數是類型參數,其類型爲class或typename
template<class T>
template<class T1, class T2>
四、模板形參在模板中做爲一種類型使用,能夠用於函數的形參、函數返回值和函數的局部變量
五、每一個模板形參要在函數的形參列表中至少出現一次
六、模板參數名的做用域侷限於函數模板的範圍內
(二)、函數模板的使用
一、函數模板爲全部的函數提供惟一的一段函數代碼,加強了函數設計的通用性
二、使用函數模板的方法是先說明函數模板,而後實例化成相應的模板函數進行調用執行
函數模板不是函數,不能被執行
置換代碼中的類型參數獲得模板函數——實例化
實例化後的模板函數是真正的函數,能夠被執行
三、模板被編譯了兩次
實例化以前,先檢查模板代碼自己,查看語法是否正確;在這裏會發現語法錯誤,若是遺漏分號等。
實例化期間,檢查模板代碼,查看是否全部的調用都有效。在這裏會發現無效的調用,如該實例化類型不支持某些函數調用或操做符等。
四、普通函數只須要聲明,便可順利編譯,而模板的編譯須要查看模板的定義(聲明和定義需放在同個.h文件)
(三)、函數模板特化
假設如今咱們有這樣一個模板函數max:
template <typename T>
const T& max(const T& a, const T& b)
{
return a < b ? b : a;
}
而後如今咱們要比較兩個字符串的大小,如:
const char* str1 = "aaa";
const char* str2 = "zzz";
此時若是按通常的實例化,比較的將是str1 和 str2 的大小,即比較指針數值大小,而不是字符串大小,故咱們須要實現一個模板函數的特化,以下:
template<>
const char* const& max(const char* const& a, const char* const& b)
{
return strcmp(a, b) < 0 ? b : a;
}
(四)、重載函數模板,非模板函數重載
C++語言能夠重載一個函數模板
用戶能夠用非模板函數重載一個同名的函數模板
max.h:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#ifndef _MAX_H_ #define _MAX_H_ #include <iostream> using namespace std; template <typename T> const T &max(const T &a, const T &b) { cout << "template max(const T& a, const T& b)" << endl; return a < b ? b : a; } // 函數模板重載 template <typename T> const T &max(const T &a, const T &b, const T &c) { cout << "template max(const T& a, const T& b, const T& c)" << endl; return ::max(a, b) < c ? c : ::max(a, b); // ::max 會調用非模板函數 } // 非模板函數重載 const int &max(const int &a, const int &b) { cout << "max(const int& a, const int& b)" << endl; return a < b ? b : a; } // 函數模板特化 template <> const char *const &max(const char *const &a, const char *const &b) { cout << "template <> max(const char* const&a, const char* const& b)" << endl; return strcmp(a, b) < 0 ? b : a; } // 非模板函數重載 const char *const &max(const char *const &a, const char *const &b) { cout << "max(const char* const&a, const char* const& b)" << endl; return strcmp(a, b) < 0 ? b : a; } #endif // _MAX_H_ |
main.cpp:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <iostream> #include <string> using namespace std; #include "max.h" class Test { public: friend bool operator<(const Test &t1, const Test &t2) { cout << "operator<(const Test& t1, const Test& t2)" << endl; return true; } }; int main(void) { //與 std::max 區別開來 cout <<::max(5.5, 6.6) << endl; // 自動推導 max(const double&, const double&); cout <<::max('a', 'c') << endl; // 自動推導 max(const char&, const char&); Test t1; Test t2; ::max(t1, t2); // Test::operator<(const Test& t1, const Test& t2) const char *str1 = "aaa"; const char *str2 = "zzz"; cout <<::max(str1, str2) << endl; //優先選擇非模板函數 cout <<::max<>(str1, str2) << endl; //指定使用模板,進而找到模板特化 // cout<<::max<const char*>(str1, str2); // 顯式指定模板特化函數max(const char* const&a, const char* const& b) cout <<::max(1, 5, 3) << endl; // 模板匹配,進而自動推導 cout <<::max('a', 50) << endl; // 'a'即97;選擇非模板函數(char能夠隱式轉換成int) cout <<::max(97, 100) << endl; // 優先選擇非模板函數 cout <<::max<>(97, 100) << endl; // 指定使用模板,進而自動推導 // cout<<::max<>('a', 50)<<endl; // Error,指定使用模板,但編譯器不知道怎樣推導 cout <<::max<int>(97, 100) << endl; // 顯式指定模板函數max(const int&, const int&) cout <<::max<int>('a', 50) << endl; // 顯式指定模板函數max(const int&, const int&) return 0; } |
函數模板能夠經過傳遞的參數類型自動推導,查看是否有合適的函數實例可用,而類模板則必須顯式說明模板的類型參數,這樣才能實例化模板類實例。
4、模板的偏特化
模板的偏特化是指須要根據模板的某些但不是所有的參數進行特化
(1) 類模板的偏特化
例如c++標準庫中的類vector的定義
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
這個偏特化的例子中,一個參數被綁定到bool類型,而另外一個參數仍未綁定須要由用戶指定。
(2) 函數模板的偏特化
嚴格的來講,函數模板並不支持偏特化,但因爲能夠對函數進行重載,因此能夠達到相似於類模板偏特化的效果。
template <class T> void f(T); (a)
根據重載規則,對(a)進行重載
template < class T> void f(T*); (b)
若是將(a)稱爲基模板,那麼(b)稱爲對基模板(a)的重載,而非對(a)的偏特化。C++的標準委員會仍在對下一個版本中是否容許函數模板的偏特化進行討論。
參考:
C++ primer 第四版 Effective C++ 3rd C++編程規範