鐵汁們,今天給大家分享一篇模板+模板特化,來吧,開造??
泛型編程
定義:編寫跟具體類型無關(guān)的通用代碼,是實(shí)現(xiàn)代碼復(fù)用的一種手段。模板是泛型編程的基礎(chǔ)。
- 問:如何實(shí)現(xiàn)一個通用的swap函數(shù)?
- 答:寫成函數(shù)模板,不能在函數(shù)重載了。原因:代碼復(fù)用率低,重載的函數(shù)僅是類型不同,若出現(xiàn)了其他新的類型,代碼的可維護(hù)性降低,若函數(shù)中有一處出錯了,可能會導(dǎo)致所有的重載函數(shù)均出錯。
模板
??模板的本質(zhì):本來應(yīng)該要由我自己寫的多份類似代碼(除類型不同,代碼功能、邏輯相同),現(xiàn)在不需要我自己去寫了,我只需要提供一個模板,編譯器根據(jù)我的實(shí)例化,幫我寫出代碼。
函數(shù)模板
定義
函數(shù)模板代表了一個函數(shù)家族,函數(shù)模板與類型無關(guān),在使用時被實(shí)例化,根據(jù)實(shí)例化的類型產(chǎn)生具體類型的函數(shù)版本。
- ??Tips : typename是用來定義模板參數(shù)的關(guān)鍵字,也可以使用class,但不能用struct代替class。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
namespace zzx {
//函數(shù)模板
template<class T>
void swap(T& left, T& right)
{
T& tmp = left;
left = right;
right = tmp;
}
}
int main()
{
int a = 3;
int b = 4;
cout <<"交換前:" << a << ' ' << b << endl;
zzx:swap(a, b);
cout << "交換后:" << a << ' ' << b << endl;
return 0;
}
原理分析
-
函數(shù)模板是藍(lán)圖,它本身不是函數(shù),是編譯器用特定的方法產(chǎn)生具體類型函數(shù)的模具。即:模板其實(shí)就是本來應(yīng)該由我們自己做的重復(fù)的事情交給了編譯器去做。
-
在編譯階段,編譯器通過實(shí)參的類型推演生成相對應(yīng)類型的函數(shù)。eg:當(dāng)用double類型的參數(shù)使用函數(shù)模板時,編譯器根據(jù)實(shí)參的類型推演出模板參數(shù)T的類型,將T確定為double類型,產(chǎn)生一份專門處理double類型的代碼。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
namespace zzx {
template<class T>
T Add(T& left, T& right)
{
return left + right;
}
double Add(double& left, double& right)
{
return left + right;
}
int Add(int& left, int& right)
{
return left + right;
}
}
int main()
{
int a = 1;
int b = 2;
cout << zzx::Add(a, b) << endl;
double c = 1.1;
double d = 2.2;
cout << zzx::Add(c, d) << endl;
return 0;
}
函數(shù)模板的實(shí)例化
定義:用不同類型參數(shù)使用函數(shù)模板時,稱為函數(shù)模板的實(shí)例化。模板參數(shù)的實(shí)例化有兩種,分別為隱式實(shí)例化和顯示實(shí)例化。
-
隱式實(shí)例化:也稱為推演實(shí)例化,編譯器根據(jù)實(shí)參的類型,推演出模板參數(shù)的實(shí)際類型。
-
顯示實(shí)例化:在函數(shù)名后加<>來指定模板參數(shù)的實(shí)際類型。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
namespace zzx {
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
}
int main()
{
cout << zzx::Add<int>(1, 2.2) << endl;
cout << zzx::Add<double>(1, 2.2) << endl;
return 0;
}
- 應(yīng)用場景一:函數(shù)參數(shù)無模板參數(shù),在函數(shù)體內(nèi)有模板參數(shù) -》只能顯示實(shí)例化,不能隱式實(shí)例化,因?yàn)闊o法通過實(shí)參的類型推演出模板參數(shù)的實(shí)際類型,需要指定模板參數(shù)的類型。
template<class T1, class T2>
void fun(int n)
{
T1 a ;
T2 b;
}
int main()
{
fun<int, double>(10);
return 0;
}
- 應(yīng)用場景一:對于stack類,若想讓棧1存int類型的數(shù)據(jù),讓棧2存double類型的數(shù)據(jù)。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
namespace zzx{
template<class T>
class stack {
T* a;
size_t top;
size_t capacity;
};
}
int main()
{
zzx::stack<int> s1;
zzx::stack<double> s2;
return 0;
}
模板參數(shù)的匹配原則
- 有現(xiàn)成的就吃現(xiàn)成,不要自己動手做 -》非模板函數(shù)和同名的模板函數(shù)同時存在時,且其他條件相同的情況下,會優(yōu)先調(diào)用非模板函數(shù)。
- 沒有現(xiàn)成的,有更合適的就吃更合適的,哪怕要自己動手做 。-》無非模板函數(shù),但與同名的模板函數(shù)參數(shù)類型匹配,就去調(diào)用模板函數(shù)。
- 沒有現(xiàn)成的,也沒更合適的,就將就吃 -》無非模板函數(shù),與同名的模板函數(shù)參數(shù)類型不匹配,但普通函數(shù)允許隱式類型轉(zhuǎn)化,就去調(diào)用普通函數(shù)。
??Tips : 模板函數(shù)不允許隱式類型自動轉(zhuǎn)化,但普通函數(shù)支持隱式類型自動轉(zhuǎn)化。
類模板
定義
- ??Tips : 類模板后面一定要加分號( ; ) 。
類模板的實(shí)例化
類模板只能顯示實(shí)例化 。類名+<>指定模板參數(shù)的實(shí)際類型。
-
顯式實(shí)例化的類型不同,它們就是不同類。
-
普通類,類名就是整個類的類型。對于類模板,類名就不是類型,類名+<數(shù)據(jù)類型>才是整個類的類型。
-
類模板中的函數(shù)要在外面進(jìn)行定義時,即:聲明和定義分離。聲明和定義要放在同一文件中,且需要在函數(shù)名前加上類名<數(shù)據(jù)類型>: : 。
非類型模板參數(shù)
-
模板參數(shù)可以分為類型形參和非類型形參。
-
類型模板參數(shù):就是普通的類的模板參數(shù),模板參數(shù)聲明在template 中,T是模板參數(shù)。
-
非類型模板參數(shù)是指在使用模板時傳入整形常量,用一個常量作為類(函數(shù))模板的一個參數(shù),該常量只能為整形常量,在編譯期間就可以確定其值。用于指定模板的一些具體細(xì)節(jié),如數(shù)組的長度或函數(shù)的輸入?yún)?shù)類型。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<array>
using namespace std;
namespace zzx{
//非類型模板參數(shù),只能是整形常量
template<class T, size_t N = 10>
class array {
private:
T a[N]; //定義一個靜態(tài)數(shù)組
size_t size;
};
}
int main()
{
//array為STL中的一個容器,底層為靜態(tài)數(shù)組,不會初始化,只會在越界的時候進(jìn)行檢查報錯
zzx::array<int, 5> _a1;
std::array<int, 6> _a2;
_a2[5];
return 0;
}
- array為STL中的一個容器,底層為靜態(tài)數(shù)組,不會初始化,只會在越界的時候進(jìn)行檢查,程序會崩潰。
模板特化
定義
定義:在原模板類(函數(shù))的基礎(chǔ)上,針對特殊類型進(jìn)行特殊化處理的實(shí)現(xiàn)方式。模板特化劃分為函數(shù)模板的特化和類模板的特化。
函數(shù)模板特化
1.函數(shù)模板特化的步驟
-
必須先需要一個原函數(shù)模板。
-
關(guān)鍵字template 后+ <>,函數(shù)名后+<特化的類型>。
-
函數(shù)的形參表的格式、函數(shù)內(nèi)部的實(shí)現(xiàn)邏輯必須要與原函數(shù)模板參數(shù)格式、內(nèi)部實(shí)現(xiàn)邏輯一致,但如果模板參數(shù)的類型為const T&,T為Date*,此時特化后的函數(shù)模板參數(shù)的類型為T,防止類型轉(zhuǎn)換以及常引用導(dǎo)致結(jié)果出錯。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d)const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
template<class T> //函數(shù)模板
bool Less (const T& x, const T& y)
{
return x < y;
}
int main()
{
Date d1(2022, 7, 8);
Date d2(2022, 7, 10);
cout << Less(d1, d2) << endl; //結(jié)果正確
//按地址比較了,沒有按指針指向的內(nèi)容去比較
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; //結(jié)果錯誤
Date* p3 = new Date(2022, 7, 8); //連續(xù)兩次new出來的地址大小無大小關(guān)系,new在堆上隨意取一塊地址
Date* p4 = new Date(2022, 7, 10);
cout << Less(p3, p4) << endl; //結(jié)果錯誤
return 0;
}
template<class T> //函數(shù)模板
bool Less(T& x, T& y)
{
return x < y;
}
//template<> //對函數(shù)模板進(jìn)行特化
//bool Less<Date*>(Date* x, Date* y)
//{
// return *x < *y;
//}
bool Less(Date* x, Date* y) //普通函數(shù),專門用來處理Date*的比較
{
return *x < *y;
}
- Less(p1, p2)結(jié)果錯誤,是因?yàn)榇颂幦フ{(diào)用Less函數(shù)模板,編譯器根據(jù)實(shí)參的類型推演出T的類型為Date*,在Less內(nèi)部是按照指針的值(地址)比較的,而不是按照指針指向的內(nèi)容比較的。
- 一般不建議對函數(shù)模板進(jìn)行特化,而是直接將該函數(shù)實(shí)現(xiàn)給出,代碼的可讀性高。
類模板特化
- 全特化:模板參數(shù)列表中所有參數(shù)的類型全部都是確定的類型。
//全特化
template<>
class AA<char, int>
{
public:
AA() { cout << "AA<char, int>" << endl; }
private:
char _d1;
int _d2;
};
int main()
{
AA<char, int> a2;
return 0;
}
- 偏特化有兩種,一種是特化部分參數(shù),另一種是對參數(shù)類型進(jìn)行條件限制,如:將參數(shù)類型偏特化為指針或者是引用類型等。
template<class T1>
class AA<T1, int>
{
public:
AA() { cout << "AA<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
int main()
{
AA<char, int> a2;
return 0;
}
//偏特化2:對參數(shù)類型進(jìn)行條件限制
template<class T1, class T2>
class AA<T1*, T2*> //兩個參數(shù)偏特化為指針類型
{
public:
AA() { cout << "AA<T1*, T2*>" << endl; }
private:
T1 _d1;
int _d2;
};
template<class T1, class T2>
class AA<T1&, T2&> //兩個參數(shù)偏特化為引用類型
{
public:
AA() { cout << "AA<T1&, T2&>" << endl; }
private:
T1 _d1;
int _d2;
};
int main()
{
AA<int*, char*> a1;
AA<int&, char&> a2;
return 0;
}
- ??匹配順序:全特化 > 偏特化 > 普通類模板。
//類模板
template<class T1, class T2>
class AA
{
public:
AA() { cout << "AA<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
//全特化
template<>
class AA<char, int>
{
public:
AA() { cout << "AA<char, int>" << endl; }
private:
char _d1;
int _d2;
};
//偏特化
template<class T1>
class AA<T1, int>
{
public:
AA() { cout << "AA<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
int main()
{
AA<char, int> a1; //全特化
AA<int, int> a2; //偏特化
AA<int, char> a3; //普通類模板
return 0;
}
應(yīng)用實(shí)例
- 場景一:處理優(yōu)先隊(duì)列中T為Date*的數(shù)據(jù)。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<queue>
using namespace std;
int main()
{
priority_queue<Date*> pq;
pq.push(new Date(2022, 7, 8));
pq.push(new Date(2022, 7, 10));
pq.push(new Date(2022, 7, 6));
while (!pq.empty())
{
cout << *pq.top() << endl;
pq.pop();
}
return 0;
}
-
原因分析:因?yàn)?priority_queue<class T, class Container = vector, class Compare = less>,此處默認(rèn)去調(diào)用仿函數(shù)less,編譯器根絕實(shí)參的類型推演出T為Date*,導(dǎo)致Less內(nèi)部是按照指針的值(地址)比較的,而不是按照指針指向的內(nèi)容比較的。
-
解決方法:手動寫個專門處理Date*類型的仿函數(shù) 或者 對仿函數(shù)類進(jìn)行特化。
class less { //小于
public:
bool operator()(const Date* x, const Date* y)
{
return *x < *y;
}
};
namespace zzx {
template<class T>
class less { //小于
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<>
class less<Date*>
{ //小于
public:
bool operator()(Date* x, Date* y)
{
return *x < *y;
}
};
}
- 場景二:處理優(yōu)先隊(duì)列中T為任意類型*的數(shù)據(jù)。采用偏特化的第二種。
namespace zzx {
template<class T>
class less { //小于
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T> //偏特化2
class less<T*>
{ //小于
public:
bool operator()(T* x, T* y)
{
return *x < *y;
}
};
}
int main()
{
priority_queue<Date*, vector<Date*>, zzx::less<Date*>> pq1;
pq1.push(new Date(2022, 7, 8));
pq1.push(new Date(2022, 7, 10));
pq1.push(new Date(2022, 7, 6));
while (!pq1.empty())
{
cout << *pq1.top() << endl;
pq1.pop();
}
priority_queue<int*, vector<int*>, zzx::less<int*>> pq2;
pq2.push(new int(1));
pq2.push(new int(2));
pq2.push(new int(3));
while (!pq2.empty())
{
cout << *pq2.top() << endl;
pq2.pop();
}
return 0;
}
模板分離編譯
原理分析
-
分離編譯模式:一個項(xiàng)目由若干個文件組成,而每個源文件單獨(dú)編譯生成目標(biāo)文件,最后將所有的目標(biāo)文件鏈接起來形成一個單一的可執(zhí)行文件的過程稱為分離編譯模式。
-
模板分離編譯:模板的聲明和定義分離。將模板的聲明放在頭文件.h文件,把模板的定義放在源文件.cpp文件。
解決方法
- 在模板的定義位置處顯示實(shí)例化,進(jìn)行類型聲明,但這種方法不實(shí)用。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
using namespace std;
#include"Add.h"
namespace zzx{
//顯示實(shí)例化
template
int Add<int>(const int&, const int&);
template
double Add<double>(const double&, const double&);
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
}
- 將模板的定義和聲明放在同一個文件中,將該文件定義為“.hpp"或者“.h"。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
namespace zzx {
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
}
模板總結(jié)
-
優(yōu)點(diǎn):模板復(fù)用了代碼,節(jié)省了資源。增強(qiáng)了代碼的靈活性。
-
缺點(diǎn):會導(dǎo)致代碼膨脹,也會導(dǎo)致編譯時間變長。不易定位錯誤(解決方法:排除法,一段一段注釋,寫一部分就編譯一部分。
??Tips:本質(zhì):本來應(yīng)該由我們自己去寫的多份類似代碼,我們只需要提供一個模具,讓編譯器根據(jù)實(shí)例化幫我們?nèi)憽?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-842865.html
鐵鐵們,模板+模板特化就到此結(jié)束啦,若博主有不好的地方,請指正,歡迎鐵鐵們留言,請動動你們的手給作者點(diǎn)個??鼓勵吧,你們的鼓勵就是我的動力?文章來源地址http://www.zghlxwxcb.cn/news/detail-842865.html
到了這里,關(guān)于【C++】模板+模板特化的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!