日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

C++模板:模板简述,函数模板详细说明【C++模板】(56)

發布時間:2025/3/20 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++模板:模板简述,函数模板详细说明【C++模板】(56) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  • 模板
  • 模板語義
  • 函數模板
    • 重載泛化
    • 函數模板
      • 語法
      • 模板泛化
      • 特性小結
      • 編譯原理
  • 函數模板應用
    • 算法抽象
    • 快速排序算法實現模板化
    • 函數模板默認參數
    • 函數模板的特化
  • 函數模板適用場景

模板

模板語義

模板是一門語言,支持泛型的語法基礎。

泛型(Generic Programming),是指在多種數據類型上皆可操作的含意。泛型編程的代表作品 STL 是一種高效、泛型、可交互操作的軟件組件。

泛型編程最初誕生于 C++中,目的是為了實現 C++的 STL(標準模板庫)。其語言支持機制就是模板(Templates)。

模板的精神其實很簡單:類型參數化(type parameterized),即類型也是一種參數,也是一種靜多態。

換句話說,把一個原本特定于某個類型的算法或類當中的類型信息抽掉,抽象出來做成模板參數 T。

函數模板

重載泛化

重載函數,雖然實現了泛化的一些特性,但是不徹底,且有二義性(ambiguous)的存在。

代碼演示:

#include <iostream> using namespace std;void myswap(int & a, int & b) {int t = a;a = b;b = t; }void myswap(double & a, double & b) {double t = a;a = b;b = t; } int main() {double a = 2; double b = 3;myswap(a, b);cout << a << " " << b << endl;int aa = 2; int bb = 3;myswap(aa, bb);cout << aa<<" " << bb << endl;return 0; }

運行結果為:

如下,示例中 long 類型可以隱式的轉化為 int 或是 double,編譯會出現二義性,導致編譯失敗。
代碼演示:

#include <iostream> using namespace std;void myswap(int & a, int & b) //函數重載 {int t = a;a = b;b = t; }void myswap(double & a, double & b) //函數重載 {double t = a;a = b;b = t; }int main() {long a = 2; long b = 3;myswap(a, b);//ambiguousreturn 0; }

編譯器報錯:

可以增加函數重載類型來解決上面的問題,也是一種最好的解決方案。

例如增加:

void myswap(long & a, long & b) //函數重載 {long t = a;a = b;b = t; }

也可以使用模板來解決函數重載的泛化不徹底的問題。
使用模板實現徹底泛化。

函數模板

語法

//在一個函數的參數表,返回類型和函數體中使用參數化的類型。 template<typename 類型參數 T1,typename/class 類型參數 T2,...> 返回類型 函數模板名(函數參數列表) {函數模板定義體 }

模板泛化

template,既可以與函數同行,也可以將函數另起一行來書寫。T 即為范化的類型。

其過程,相當于經歷了兩次編譯,先依據實參類型,實例化函數,然后再將實例化的函數,參與編譯。

代碼演示:

#include <iostream> using namespace std;template<typename T> void myswap(T & a, T & b) {T t = a;a = b;b = t; }int main() {long a = 2; long b = 3;myswap(a, b);cout << a << " " << b << endl;return 0; }

運行結果:

我們說重載對于泛化不徹底,模板徹底實現泛化,我們接下來進行說明:
代碼演示:

#include <iostream> using namespace std;template<typename T> void myswap(T & a, T & b) {T t = a;a = b;b = t; }int main() {long a = 2; long b = 3;myswap(a, b);cout << a << " " << b << endl;string s1 = "hello";string s2 = "world";myswap(s1,s2);cout << s1 << " " << s2 << endl;double d1 = 3.1;double d2 = 4.1;myswap(d1, d2);cout << d1 << " " << d2 << endl;return 0; }

運行結果:

那么就會引發我們思考:
模板是不是悄悄的把我們學習過的所有類型全部重載了一遍呢?

我們通過代碼進行驗證:自實現類型使用模板。
代碼演示:

#include <iostream> using namespace std;class MyType { public:MyType(int x,int y):_x(x), _y(y){}int _x;int _y; };template<typename T> void myswap(T & a, T & b) {T t = a;a = b;b = t; }int main() {long a = 2; long b = 3;myswap(a, b);cout << a << " " << b << endl;string s1 = "hello";string s2 = "world";myswap(s1,s2);cout << s1 << " " << s2 << endl;double d1 = 3.1;double d2 = 4.1;myswap(d1, d2);cout << d1 << " " << d2 << endl;MyType mt1(1, 2);MyType mt2(3, 4);myswap(mt1, mt2);cout << mt1._x <<" "<<mt1._y << endl;return 0; }

運行結果:

我們可以看到,不只是系統提供的數據類型可以實現模板泛化,自定義的類也可以實現模板泛化。

所以模板解決了所有類型的操作,不管是基礎類型還是自定義類型。

模板的原理:

其實模板的使用本來應該是如下的使用方式:
代碼演示:

#include <iostream> using namespace std;class MyType { public:MyType(int x,int y):_x(x), _y(y){}int _x;int _y; };template<typename T> void myswap(T & a, T & b) {T t = a;a = b;b = t; }int main() {long a = 2; long b = 3;myswap<long>(a, b);cout << a << " " << b << endl;string s1 = "hello";string s2 = "world";myswap<string>(s1,s2);cout << s1 << " " << s2 << endl;double d1 = 3.1;double d2 = 4.1;myswap<double>(d1, d2);cout << d1 << " " << d2 << endl;MyType mt1(1, 2);MyType mt2(3, 4);myswap<MyType>(mt1, mt2);cout << mt1._x <<" "<<mt1._y << endl;return 0; }

運行結果:

只不過現在類型推導足夠強大了,所以

myswap<long>(a, b); myswap<string>(s1,s2); myswap<double>(d1, d2); myswap<MyType>(mt1, mt2);

就省略成了:

myswap(a, b); myswap(s1,s2); myswap(d1, d2); myswap(mt1, mt2);

上面代碼的過程,相當于經歷了兩次編譯,先依據實參類型,實例化函數。然后再將實例化的函數,參與編譯。

函數模板myswap
–>實例化為
模板函數myswap < int >
–>模板函數的調用myswap < int >( 1 , 2 )

一般來說模板經歷兩個過程:
編譯器編譯檢查模板的語法是否正確,語法通過時。當發生某一具體調用時,根據特定類型(類型由我們明確的告訴編譯器或者進行自動類型推導)然后產生特定類型的模板函數版本用來調用。

什么叫做類型參數化呢?
在模板中可以設置默認類型:
代碼演示:

template<typename T = int> void myswap(T & a, T & b) {T t = a;a = b;b = t; } template<typename T = int>

一般情況下不給默認值。

myswap<long>(a, b);

也不寫類型。

因為類型推導已經強大到不需要寫。

那么也就是把類型當作參數傳遞給函數模板,然后函數模板實例化為模板函數進行調用。

特性小結

先實例化,再調用。
嚴格匹配,不存在隱式轉化。
尺有所短,寸有所長。

編譯原理

編譯器遇到模板方法定義時,會進行語法檢查,但是并不編譯模板。編譯器無法編譯模板定義,因為它不知道使用什么類型。比如 T a,b; 在不知曉 T 具體類型時,是無法分配內存,更無從談編譯 a = b;

T 獲取類型的過程,稱為模板的實例化,函數模板通過具體類型產生不同的函數;編譯器會對函數模板進行兩次編譯:在聲明的地方對模板代碼本身進行編譯(類型檢查),在調用的地方對參數替換后的代碼進行編譯(代碼生成)。

那么模板既然這么好,模板是不是就沒有缺陷了呢?
編譯器并不是把函數模板處理成能夠處理任意類的函數。比如,自定義類型,如何處理呢?

代碼演示:

#include <iostream> using namespace std;class MyType { public:MyType(int x,int y):_x(x), _y(y){}int _x;int _y; };template<typename T = int> void myswap(T & a, T & b) {T t = a;a = b;b = t; }int main() {int i = 3;double j = 1.2;myswap(i, j);return 0; }

編譯器報錯:

那么我們可以不可以傳遞兩個類型來實現模板呢?
代碼演示:

#include <iostream> using namespace std;class MyType { public:MyType(int x,int y):_x(x), _y(y){}int _x;int _y; };template<typename T1 , typename T2> void myswap(T1 & a, T2 & b) {T t = a; //類型應該使用 T1 呢?還是使用T2呢?a = b;b = t; }//實現比較的函數 T Max(T1 & a, T2 & b) //返回類型應該使用 T1 呢?還是使用T2呢? {}int main() {int i = 3;double j = 1.2;myswap(i, j);return 0; }

結論是不可以,詳細原因在代碼中已經給出。

模板參數做返回類型需要編譯器支持類型推導,需要確定返回類型來解決。

函數模板應用

算法抽象

數據類型的泛化,使的模板特別適合于作算法的抽象。
STL 中的 algorithm 就是典型的代表。
比如,算法 sort 就是支持類型泛化的代表排序算法。

例如:我把東西從A地搬到B地我只考慮怎么搬更省力而不考慮搬的是什么東西。

快速排序算法實現模板化

將快速排序算法實現模板化。此時,無論轉入的是 int 的還是 float 的類型,均是可以處理。相同邏輯的函數沒有必要寫兩遍。

代碼演示:

#include <iostream> #include <typeinfo> using namespace std; template<typename T> void quickSort(T* array, int left, int right) {if (left < right){int low = left; int high = right;T pivot = array[low];while (low < high){while (array[high] >= pivot && high > low)high--;array[low] = array[high];while (array[low] <= pivot && high > low)low++;array[high] = array[low];}array[low] = pivot;quickSort(array, left, low - 1);quickSort(array, low + 1, right);} }int main() {int array[10] = { 1,3,5,7,2,4,6,8,0,9 };quickSort(array, 0, 9);for (auto i : array){cout << i << endl;} }

運行結果:

那么我們對于數據類型進行修改:
代碼演示:

#include <iostream> #include <typeinfo> using namespace std; template<typename T> void quickSort(T* array, int left, int right) {if (left < right){int low = left; int high = right;T pivot = array[low];while (low < high){while (array[high] >= pivot && high > low)high--;array[low] = array[high];while (array[low] <= pivot && high > low)low++;array[high] = array[low];}array[low] = pivot;quickSort(array, left, low - 1);quickSort(array, low + 1, right);} } int main() {char array[10] = { 'j','g','f','r' ,'d' ,'e' ,'w' ,'h' ,'j' ,'j' };quickSort(array, 0, 9);for (auto i : array){cout << i << endl;} }

運行結果為:

也可以對于字符串進行排序:
對于main函數進行修改:

int main() {string array[10] = { "sdf" ,"gfsd" ,"rewt" ,"yter" ,"ndfg" ,"asdf" ,"hgjf" ,"sdfg" ,"jrrt" ,"hike" };quickSort(array, 0, 9);for (auto i : array){cout << i << endl;} }

運行結果:

寫模板時,先用基本類型實現,然后套用模板,然后進行測試。

函數模板默認參數

重在理解,類型參數化,此時的默認參數,不再是一數值,而是類型。

函數模板,在調用時,先實例化為模板函數,然后再調用。當然也可以設置默認類型的默認值。由于系統強大的自動推導能力,有時默認也沒有太大的意義。

template<typename T = int> void quickSort(T * array,int left, int right)

函數模板的特化

Template Specialization,函數模板的特化,即函數模板的特殊情況,個例行為。

就是在實例化模板時,對特定類型的實參進行特殊處理,即實例化一個特殊的實例版本。

template<typename T> int compare( T &a,T &b) template<> int compare <const char*>( const char*&a, const char*&b)

當以特化定義時的形參使用模板時,將調用特化版本,模板特化分為全特化和偏特化,函數模板的特化,只能全特化;

比如,我們在比較兩個數的大小時:
代碼演示:

#include <iostream> #include <string.h> using namespace std;template<typename T> int compare(T& a,T& b) {if (a > b) return 1;else if (a < b)return -1;else return 0; }int main() {int a = 10;int b = 20;cout << compare(a, b) << endl;string sa = "abcd";string sb = "abc";cout << compare(sa, sb) << endl;return 0; }

運行結果:

上面的代碼是模板的正常使用,沒有問題。

我們對于代碼進行修改:

#include <iostream> #include <string.h> using namespace std;template<typename T> int compare(T& a,T& b) {if (a > b) return 1;else if (a < b)return -1;else return 0; }int main() {string sa = "abcd";string sb = "abc";cout << compare(sa, sb) << endl;const char * ca = "abcd";const char * cb = "abc";cout << compare(ca, cb) << endl;return 0; }

運行結果:

我們可以看到上面的測試運行出來了不同的結果。

原因分析:
實參為兩個 char * 時,比較的是指針地址的大小,而不是指針指向內容的大小。

此時就需要為該函數模板定義一個特化版本,即特殊處理的版本:

代碼演示:

#include <iostream> #include <string.h> using namespace std;template<typename T> int compare(T& a, T& b) {if (a > b) return 1;else if (a < b)return -1;else return 0; }//模板特化版本 template<> int compare < const char* >(const char*& a, const char*& b) {return strcmp(a, b); }int main() {const char * ca = "abcd";const char * cb = "abc";cout << compare(ca, cb) << endl;return 0; }

運行結果:

函數模板適用場景

函數模板,只適用于函數的參數個數相同而類型不同,且函數體相同的情況。

總結

以上是生活随笔為你收集整理的C++模板:模板简述,函数模板详细说明【C++模板】(56)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。