C++模板進階
# C++模板初階
1.非型別模板引數
template<class T, size_t N>
class Array
{
private:
T arr[N];
};

模板引數分為型別引數和非型別引數,如上述所述代碼就是非型別模板引數
非型別引數:出現在模板的引數類表中,更在class或者typename之類的引數型別名稱
非型別引數就是作為一個類(函式)的引數,在模板中該引數可以被當作常量來使用
比如庫里面的Array就是使用非型別模板引數來完成的
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xkB96ajG-1631671145206)(https://raw.githubusercontent.com/qingyan520/Cloud_img/master/img/image-20210914202755252.png)]](https://img.uj5u.com/2021/09/16/265090161037038.png)
當然,非型別模板也可以有默認的預設值
#include<iostream>
using namespace std;
namespace hello
{
template<class T, size_t N=100>
class Array
{
private:
T arr[N];
};
}
int main()
{
hello::Array<int>arr;
return 0;
}

注意:
1.浮點數,類物件以及字串是不容許作為非型別模板引數的
2.非型別的模板引數必須在編譯器就能確認結果
2.模板的特化
在某些情況下,使用模板可能會出現一些錯誤的結果,例如下面對兩個字串的比較
//例如利用模板來比較兩個字串是否相等
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
return left == right;
}
int main()
{
int a = 10;
int b = 10;
cout << Is_Same<int>(a,b) << endl;//結果為1
char str1[] = "hello";
char str2[] = "hello";
cout << Is_Same(str1, str2) << endl;//結果為0
return 0;
}

上面程式運行時會分別列印1,0,這說明我們在比較str1和str2這兩個字串時出現了錯誤,為什么會出現str1和str2不相等的情況呢?
陣列名是陣列首元素的地址,是一個指標,在模板引數進行實體化的時候,模板引數T會被替換成一個char*型別的指標,而這兩個字符陣列都是位于堆疊區的,二者地址不相同,所以會回傳false
而這種特殊情況就需要對模板進行特化,即:在原模版的基礎上,針對特殊型別所進行特殊化的實作方式,分為函式模板特化和類模板特化
1.函式模板特化
步驟:
1.首先必須現有一個基礎的函式模板
2.template后面接一對尖括號<>
3.函式名后面跟一對健康括號,尖括號內指定需要特化的型別
4.函式形參串列:必須要和函式模板的基礎引數型別完全相同,如果編譯器不同可能報一些奇怪的錯誤
下面,我們對上面哪個求兩個字串是否相等做一些改變:
//方法一:直接重新定義一個同名函式,里面引數寫出char*,當我們執行Is_Same函式時,編譯器首先會在非模板函式中查找是否有能夠進行匹配的,如果引數匹配直接執行該非模板函式,不匹配則在模板函式中尋找
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
return left == right;
}
//重新定義一個函式判斷兩個字串是否相等
bool Is_Same(char* left,char* right)
{
if (strcmp(left, right) == 0)
return true;
return false;
}
int main()
{
int a = 10;
int b = 10;
cout << Is_Same<int>(a,b) << endl;
char str1[] = "hello";
char str2[] = "hello";
cout << Is_Same(str1, str2) << endl;
return 0;
}

//方法二:按照上面所說的函式模板的特化重新寫一個函式模板的特化
template<>
bool Is_Same<const char*const>(const char* const &left, const char* const &right)
{
if (strcmp(left, right) == 0)
return true;
return false;
}
2.類模板的特化
//最簡單的類模板
template<class T1, class T2>
class A
{
private:
T1 a;
T2 b;
};
1.全特化
全特化即將模板引數串列的所有引數都進行確定
//例如:
//在A<int,int>下我們可以執行自己想要做的是
//在A<int,double>下完成另外一件事
#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{
public:
A()
{
cout << "A<T1,T2>" << endl;
}
};
template<>
class A<int, int>
{
public:
A()
{
cout << "A<int ,int>" << endl;
}
};
template<>
class A<int, double>
{
public:
A()
{
cout << "A<int,double>" << endl;
}
};
int main()
{
A<int, int>a;
A<int, double>b;
A<char, int>c;
return 0;
}

2.偏特化
即給定類模板一般引數
1.部分特化:將模板引數串列中的一部分引數進行特化
class A<T1,int>
{
public:
A()
{
cout << "A<T1,int>" << endl;
}
};
2.引數進一步限制
偏特化不僅僅使之特化部分引數,而是正對模板引數更近一步的條件限制所設計出來的一個特化版本
//兩個引數偏特化為指標型別
template<class T1,class T2>
class A<T1*,T2*>
{
public:
A()
{
cout << "A<T1*,T2*>" << endl;
}
};
//兩個引數偏特化為參考型別
template<class T1, class T2>
class A<T1& ,T2&>
{
public:
A()
{
cout << "A<T1&,T2&>" << endl;
}
};
//一個引數偏特化為參考,另外一個引數偏特化為指標
template<class T1,class T2>
class A<T1&,T2*>
{
public:
A()
{
cout << "A<T1&,T2*>" << endl;
}
};

3.模板的分離編譯
1.模板的分離編譯即test.h中寫類模板的宣告,test.cpp中寫類模板的實作,main.cpp中寫主要函式功能
-------test.h---------
模板的定義
--------test.cpp-------
模板的實作
--------main.cpp--------
模板的呼叫
2.在書寫模板類時不能采取這種方式,會出現鏈接錯誤

一個c/c++程式的執行大概遵守以上圖片的程序,經過預處理,編譯,匯編,鏈接,最終形成可執行程式
test.h中放函式的宣告,test.cpp中放函式的實作,main.c中呼叫函式,經過預處理,編譯,匯編之后會生成test.o和main.o,當呼叫這個模板函式時,發現當前只有函式的宣告,則回去test.o的符號表中去找,因為模板只有在實體化時才能生成對應的代碼,符號表里面沒有該模板函式的地址,就會出現鏈接錯誤
3.解決方法
1.將宣告和定義放到檔案"xxx.hpp"里面或者xxx.h中
2.模板定義的位置顯式實體化
ps:推薦直接宣告與定義直接放在.h檔案中
歡迎大家的觀看,期待下次重逢 To Be Continued…
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300518.html
標籤:其他
上一篇:Python中的回圈結構
下一篇:ROS從入門到放棄(學習筆記1)
