1. 程式人生 > >模板與泛型編程1(函數模板)

模板與泛型編程1(函數模板)

開始 name 對象 AC pan pen != 函數的參數 接受

定義、實例化函數模板:

對於函數體完全相同,唯一差異就是參數類型的情況,我們可以定義一個通用的函數模板,而非為每個類型都定義一個新函數:

技術分享圖片
 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 template <typename T>//模板參數列表
 6 int compare(const T &v1, const T &v2) {
 7     if(v1 < v2) return -1;
 8     if(v2 < v1) return
1; 9 return 0; 10 } 11 12 int main(void) { 13 cout << compare(1, 0) << endl;//T為int 14 cout << compare(vector<int>{1, 2, 3}, vector<int>{2, 3, 4}) << endl;//T為vector<int> 15 16 return 0; 17 }
View Code

註意:模板定義以關鍵字 template 開始,後跟一個模板參數列表,裏面有一個或多個模板參數

在模板定義中,模板參數列表不能為空

類似於函數參數,模板參數表示在類或函數定義中用到的類型或值。當使用模板時,我們 (隱式的或顯式地) 指定模板實參,將其綁定道模板參數上。

當我們調用一個函數模板時,編譯器(通常)用函數實參來為我們推斷模板實參,並用推斷出的模板參數來為我們實例化一個特定版本的函數。

模板參數可以是模板類型參數或非類型模板參數(表示值而非類型)

模板類型參數:

前面的 compare 中 T 就是一個模板類型參數。一般來說我們可以將類型參數看作類型說明符,就像內置類型或類類型說明符一樣使用

技術分享圖片
1 template <typename T>//類型參數
2 //
函數模板返回類型與參數類型相同 3 T foo(T *p) {//類型參數可硬用來指定返回類型或函數的參數類型 4 T tmp = *p;//類型參數可以用於函數體內部聲明變量或類型轉換 5 //... 6 return tmp; 7 }
View Code

註意:類型參數可用來指定返回類型、函數的參數類型、聲明變量或類型轉換

類型參數前必須使用關鍵字 class 或 typename:

// 錯誤,U 之前必須加上 class 或 typename

template <typename T, U> T calc(const T&, const U&);

//正確

template <typename T, class U> T calc(const T&, const U&);

註意:在模板參數列表中,class 和 typename 含義完全相同,可以互換使用

非類型模板參數:

一個非類型模板參數表示一個值而非一個類型。我們通過一個特定的類型名而非關鍵字 class 或 typename 來指定非類型參數。當一個模板唄實例化時,非類型參數被一個用戶提供的或編譯器推斷出的值所代替,這些值必須是常量表達式,從而允許編譯器在編譯時實時實例化模板:

技術分享圖片
 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4 
 5 template<unsigned N, unsigned M>
 6 int compare(const char (&p1)[N], const char (&p2)[M]) {//數組不能拷貝,可以引用
 7     return strcmp(p1, p2);
 8 }
 9 
10 int main(void) {
11     compare("hi", "mom");
12 
13     return 0;
14 }
View Code

編譯器會實例化出如下模板:

int compare(const char (&p1)[3], const char (&p2)[4])

註意:一個非類型參數可以是一個整型,或者是一個指向對象或函數類型的指針或(左值)引用

綁定到非類型整型參數的實參必須是一個常量表達式。綁定到指針或引用非類型參數的實參必須具有靜態的生存期。指針參數也可以用 nullptr 或一個值為 0 的常量表達式來實例化

inline 和 constexpr 的函數模板:

template <typename T> inline T min(const T&, const T&);
註意:inline 或 constexpr 說明符放在模板參數列表之後,返回類型之前

模板編譯:

當編譯器遇到一個模板定義時,它並不生成代碼。只有當我們實例化(使用)模板的特定版本時,編譯器才會生成代碼。

通常,當我們調用一個函數時,編譯器只需要掌握函數的聲明。類似的,當我們使用一個類型的對象時,類定義必須是可用的。但成員函數的定義不必已經出現。因此我們可以將類定義和函數聲明放在頭文件中,而普通函數和類的成員函數的定義放在源文件中。

模板則不同:為了生成一個實例化版本,編譯器需要掌握函數模板或類模板成員函數的定義。因此,與非模板代碼不同,模板的頭文件通常既包括聲明也包括定義。

大多數編譯錯誤發生在實例化期間報告:

如,前面的 compare 函數,如果我們用一個沒有定義 < 運算符的類型實例化該函數模板,則會發生編譯錯誤

定義 find 模板:

技術分享圖片
 1 #include <iostream>
 2 #include <vector>
 3 #include <list>
 4 using namespace std;
 5 
 6 template<typename T, typename U>
 7 T find(const T &bg, const T &ed, const U &val) {
 8     auto cnt = bg;
 9     while(cnt != ed) {
10         if(*cnt == val) break;
11         ++cnt;
12     }
13     return cnt;
14 }
15 
16 int main(void) {
17     vector<int> vt = {1, 2, 3, 4, 5};
18     list<string> lt = {"jhhg", "fjsl", "zzz", "jfl"};
19 
20     auto cnt = find(vt.begin(), vt.end(), 1);
21     cout << *cnt << endl;
22 
23     auto gg = find(lt.begin(), lt.end(), "zzz");
24     cout << *gg << endl;
25 
26     gg = find(lt.begin(), lt.end(), "aa");
27     if(gg == lt.end()) cout << "//" << endl;
28 
29     return 0;
30 }
View Code

編寫接受一個數組引用參數的 print 模板:

技術分享圖片
 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T, unsigned N>
 5 void print(const T (&t)[N]) {
 6     for(int i = 0; i < N; i++) {
 7         cout << t[i] << " ";
 8     }
 9     cout << endl;
10 }
11 
12 int main(void) {
13     int a[10] = {2, 2, 3};
14     print(a);
15 
16     string b[3] = {"fsl", "fj", "z"};
17     print(b);
18 
19     // char *ch = "jfk";//註意,不能傳指針,只能傳數組
20     print("fjdl");
21 
22     return 0;
23 }
View Code

定義接受一個數組實參的 begin、end 模板:

技術分享圖片
 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 template<typename T, unsigned N>
 6 T* begin_(const T (&a)[N]) {
 7     return const_cast<T*>(a);//底層const只能顯式的去除
 8 }
 9 
10 template<typename T, unsigned N>
11 T* end_(const T (&a)[N]) {
12     return const_cast<T*>(a) + N;
13 }
14 
15 template<typename T, unsigned N>
16 void print(const T (&t)[N]) {
17     for(int i = 0; i < N; i++) {
18         cout << t[i] << " ";
19     }
20     cout << endl;
21 }
22 
23 int main(void) {
24     int a[10] = {7, 1, 3, 3};
25     sort(begin_(a), end_(a));
26     print(a);
27 
28     return 0;
29 }
View Code

編寫一個 constexpr 模板,返回給定數組的大小:

技術分享圖片
 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T, unsigned N>
 5 constexpr unsigned size(const T (&t)[N]) {
 6     return N;
 7 }
 8 
 9 class gel{
10     int x;
11 };
12 
13 int main(void) {
14     gel g[10];
15     cout << size(g) << endl;
16 
17     return 0;
18 }
View Code

模板與泛型編程1(函數模板)