最近學習了一些模板的知識,速寫本博客作為學習筆記,若有興趣,歡迎垂閱讀!
?1.非類型模板參數
?模板參數分類類型形參與非類型形參。
類型形參即:出現在模板參數列表中,跟在class或者typename之類的參數類型名稱。非類型形參,就是用一個常量作為類(函數)模板的一個參數,在類(函數)模板中可將該參數當成常量來使用。
ps:
- 浮點數(C++20之前)、類對象以及字符串是不允許作為非類型模板參數的。
- 非類型的模板參數必須在編譯期就能確認結果。
namespace hd
{// 定義一個模板類型的靜態數組template<class T, size_t N = 10>//N就是非類型模板參數class array{public:T& operator[](size_t index) { return _array[index]; }const T& operator[](size_t index)const { return _array[index]; }size_t size()const { return _size; }bool empty()const { return 0 == _size; }private:T _array[N];size_t _size;};
}int main()
{hd::array<int> ia;hd::array<char, 6> ca;return 0;
}
看到這個栗子,類模板參數N就是非類型模板參數。
庫里面也有使用非類型模板參數的栗子,比如類模板array?的設計就使用了非類型模板參數,看到N就是這個類模板的非類型模板參數:
array也是一個容器,底層其實就是一個靜態數組,關于其接口有興趣的話可以自行去查閱。不過這個容器比較雞肋吧,因為vector似乎更香。
當然,這個容器也有其優點:
- 普通數組對于越界訪問的檢查是一種抽查,越界訪問了未必檢查得出來。但array對于越界訪問一查一個準,也許其底層實現加了斷言吧,例如其成員函數operator[]完全可以加斷言檢查是否越界。
- 本容器對象一旦實例化,就不支持動態調整大小,因為其空間是靜態開辟的靜態數組。但是其空間是在棧區開辟的,而vector的空間是在堆區開辟的,是動態開辟的。
看到當非類型模板參數有缺省值的一些情況,實例化對象代碼寫法:
template<class T = int, size_t N = 10>
class a
{T _arr[N];
};
template<int N = 10>
class b
{int _arr[N];
};int main()
{a<> ap;b<> bp;return 0;
}
?C++20以后也支持如下寫法:
template<class T = int, size_t N = 10>
class a
{T _arr[N];
};
template<int N = 10>
class b
{int _arr[N];
};int main()
{a ap1;a<char> ap2;b bp;return 0;
}
2.模板的特化?
2.1.模板特化的概念?
通常情況下,使用模板可以實現一些與類型無關的代碼,但對于一些特殊類型的可能會得到一些 錯誤的結果,需要特殊處理,比如:實現了一個專門用來進行小于比較的函數模板
#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h) ,_m(m) ,_s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)
{return t1 < t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//結果正確cout << Less(&t1, &t2) << endl;//結果錯誤return 0;
}
?可以看到,Less絕對多數情況下都可以正常比較,但是在特殊場景下就得到錯誤的結果。上述示 例中,&t2指向的t2顯然小于&t1指向的t1對象,但是Less內部并沒有比較&t2和&t1指向的對象內 容,而比較的是&t1和&t2本身的值,這就無法達到預期而錯誤。
此時,就需要對模板進行特化。即:在原模板類的基礎上,針對特殊類型所進行特殊化的實現方 式。模板特化中分為函數模板特化與類模板特化。
2.2.函數模板特化
?函數模板的特化步驟:
1. 必須要先有一個基礎的函數模板
2. 關鍵字template后面接一對空的尖括號<>
3. 函數名后跟一對尖括號,尖括號中指定需要特化的類型
4. 函數形參表:?必須要和模板函數的基礎參數類型完全相同,如果不同編譯器可能會報一些奇 怪的錯誤。
#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h) ,_m(m) ,_s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)//注意這里const修飾的是引用,而不是修飾類型
{return t1 < t2;
}//Less函數模板的特化
template<>
bool Less<Time*>(Time* const & t1, Time* const & t2)
{return *t1 < *t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//走模板生成cout << Less(&t1, &t2) << endl;//調用特化之后的版本,而不走模板生成了return 0;
}
但是但是,?一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為了實現簡單通常都是將該函數直接給出,而不是去特化函數模板,例如:
#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h), _m(m), _s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)//注意這里const修飾的是引用,而不是修飾類型
{return t1 < t2;
}//現成函數(非函數模板特化)
bool Less(Time* t1, Time* t2)
{return *t1 < *t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//走模板生成cout << Less(&t1, &t2) << endl;//調用現成函數return 0;
}
直接將類型是Time*類型比較的函數給出,不是香噴噴嗎?何必走函數模板特化呢?所以函數模板不建議特化。
2.3.類模板特化?
?類模板特化分為全特化和半特化(偏特化)。
2.3.1.全特化
#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//類模板全特化,當第1個模板參數為int且第2個模板參數為char時,調用
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
int main()
{Data<int, int> d1;Data<int, char> d2;return 0;
}
?2.3.2.半特化(偏特化)
?偏特化:任何針對模版參數進一步進行條件限制設計的特化版本。偏特化有以下兩種表現方式:
- 部分特化
將模板參數類表中的一部分參數特化。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//類模板半特化,只要第2個類模板參數為int,調用
template<class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};
int main()
{Data<int, int> d1;Data<int, char> d2;return 0;
}
- 參數更進一步的限制
偏特化并不僅僅是指特化部分參數,而是針對模板參數更進一步的條件限制所設計出來的一 個特化版本。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//類模板半特化,兩個參數偏特化為指針類型,只要2個類模板參數為指針,調用
template<class T1, class T2>
class Data<T1* , T2*>
{
public:Data() { cout << "Data<T1* , T2*>" << endl; }
private:T1 _d1;T2 _d2;
};
int main()
{Data<int, int> d1;Data<int*, char*> d2;return 0;
}
3.模板分離編譯?
?3.1.什么是分離編譯
一個程序(項目)由若干個源文件共同實現,而每個源文件單獨編譯生成目標文件,最后將所有 目標文件鏈接起來形成單一的可執行文件的過程稱為分離編譯模式。
3.2.模板的分離編譯
模板是不推薦分離編譯的,也就是說模板不推薦聲明和定義分離到不同文件下,如果分離了會出現鏈接問題。例如:
一個工程,有3個文件,分別是a.h、a.cpp、test.cpp。
a.h:
#pragma once//a類模板聲明
template <class T>
class a
{T _tmp;
public:a(const T& tmp = T());const T& get_tmp();
};//Add函數模板聲明
template<class T>
T Add(const T& n1, const T& n2);
a.cpp:
#include "a.h"//a類模板定義
template<class T>
const T& a<T>::get_tmp()
{return _tmp;
}template<class T>
a<T>::a(const T& tmp):_tmp(tmp)
{}//Add函數模板定義
template<class T>
T Add(const T& n1, const T& n2)
{return n1 + n2;
}
?test.cpp:
#include <iostream>
using namespace std;
#include "a.h"int main()
{a<int> x(10);cout << x.get_tmp() << endl;cout << Add(1, 2) << endl;return 0;
}
這3個文件中有2個模板,聲明和定義都分離了。編譯會出現問題:
如何解決??
?2個辦法:
- 將聲明和定義放到一個文件 "xxx.hpp" 里面或者xxx.h其實也是可以的,也就是說模板的聲明和定義不要分離到不同文件。推薦使用這種辦法。
- 模板定義的位置顯式實例化。這種方法不實用,不推薦使用,如下:
a.cpp:
#include "a.h"//a類模板定義
template<class T>
const T& a<T>::get_tmp()
{return _tmp;
}template<class T>
a<T>::a(const T& tmp):_tmp(tmp)
{}//Add函數模板定義
template<class T>
T Add(const T& n1, const T& n2)
{return n1 + n2;
}//顯示實例化
template
class a<int>;//顯示實例化
template
int Add(const int& n1, const int& n2);
?模板聲明和定義分離到不同文件下,且不在模板定義的位置顯示實例化的話,會出現鏈接錯誤的原因的話,感興趣可以自己去查閱哈,我就不介紹了。
感謝閱讀,歡迎斧正!