模板是C++泛型編程編程的基礎(chǔ),STL從頭到尾都是模板泛型編程
函數(shù)模板
template<class T>
T add(T a, T b) { return a + b;}
int main()
{
int a = 1, b = 2;
std::cout << add(a + b) << std::endl;
return 0;
}
類模板
template<class T>
class A
{
public:
explicit A(T val) : t(val) { }
T add(T x) { return t + x; }
private:
T t;
};
int main()
{
A<int> a(10);
std::cout << a.add(5) << std::endl;
return 0;
}
幾個(gè)需要注意的點(diǎn)
1. 類模板的和函數(shù)模板都必須定義在.h頭文件中
2. 模板的實(shí)例化類型確定是在編譯期間
3. 只是模板寫(xiě)好了,編譯一般不會(huì)很多出錯(cuò),出錯(cuò)一般會(huì)在實(shí)例化編譯之后
4. 模板實(shí)例化只會(huì)實(shí)例化用到的部分,沒(méi)有用到的部分將不會(huì)被實(shí)例化
模板特化
函數(shù)模板全特化
template< > // 全特化 注意語(yǔ)法
double add(double a, double b) { return a + b; }
int main()
{
int x = 10, y = 20;
double z = 1.1, w = 2.2;
std::cout << add(x, y) << std::endl; // 調(diào)用普通版本
std::cout << add(z, w) << std::endl; // 調(diào)用全特化版本
return 0;
}
函數(shù)模板重載(不存在偏特化)
template<class T1> // 重載版本,接收參數(shù)為指針
T1 add(T1* a, T1* b) { return *a + *b; }
int main()
{
int a = 10, b = 20;
int *x = &a, *y = &b;
add(a, b); // 調(diào)用普通模板
add(x, y); // 調(diào)用重載的模板
return 0;
}
類模板的偏特化
- 形式一
template<class T1, class T2> // 普通版本,有兩個(gè)模板參數(shù)
class B { ..... };
template<class T2> // 偏特化版本,指定其中一個(gè)參數(shù),即指定了部分類型
class B<int , T2> { ..... }; // 當(dāng)實(shí)例化時(shí)的第一個(gè)參數(shù)為int 則會(huì)優(yōu)先調(diào)用這個(gè)版本
- 形式二
template<class T> // 普通版本
class B { ..... };
template<class T> //這個(gè)偏特化版本只接收指針類型的模板實(shí)參
class B<T*> { ..... };
template<class T>
class B<T&> { ..... }; // 這個(gè)偏特化版本只接受引用類型的模板實(shí)參
- 形式三
template<class T> //普通版本
class B { ..... };
template<class T> // 這種只接受用T實(shí)例化的vector的模板實(shí)參.也是一種偏特化
class B<vector<T>> { ...... };
幾個(gè)值得注意的地方
1. 特例化本質(zhì)上是我們頂替了編譯器的工作,我們幫編譯器做了類型推導(dǎo)
2. 全特化本質(zhì)上是一個(gè)實(shí)例,而偏特化本質(zhì)上還是一個(gè)模板,只是原來(lái)模板的一個(gè)子集
3. 所以全特化的函數(shù)模板,本質(zhì)上是實(shí)例,從而不會(huì)與函數(shù)模板產(chǎn)生二義性
4. 若想讓用戶能使用特例化版本,特例化版本必須與模板定義在同一個(gè).h頭文件中
偏特化在STL中最重要的兩個(gè)應(yīng)用
1. 應(yīng)用在迭代器設(shè)計(jì)中,為了使迭代器既可以萃取出值類型,又可以包容原生指針
如果要通過(guò)一個(gè)迭代器就能知道它的值類型,那么一般會(huì)使用iterator_traits
迭代器萃取技術(shù)的兩個(gè)核心是:
a. 在每個(gè)迭代器類中定義value_type值類型的類型成員,這樣直接通過(guò)迭代器的value_type類型成員就可以知道值類型
b. 問(wèn)題就在于,迭代器必須兼容原生指針,而原生指針很難被重新定義,即要在原生指針的類中添加value_type的值類型的類型成員.這時(shí)候,靠的就是類模板的偏特化了.新添加一層iterator_traits類,專門(mén)萃取迭代器的屬性,然后再對(duì)iterator_traits類設(shè)計(jì)原生指針與原生引用的偏特化版本,就解決了這個(gè)棘手的問(wèn)題
2.type_traits類型萃取,對(duì)待特殊類型,特殊處理,提高效率
對(duì)于沒(méi)有構(gòu)造函數(shù),析構(gòu)函數(shù)等的內(nèi)置類型,如果與復(fù)雜類型一樣,執(zhí)行同樣的操作,顯然是效率不高的先實(shí)現(xiàn)一個(gè)對(duì)所有類型都設(shè)置一個(gè)最保守值的type_traits模板類,然后再對(duì)每個(gè)內(nèi)置類型設(shè)置偏特化版本,內(nèi)置類型設(shè)置一個(gè)更為激進(jìn)的值,表明可以采取更為高效的操作來(lái)提高效率比如copy函數(shù),如果傳遞的對(duì)象是一個(gè)復(fù)雜類型,那么可能只能采取最保守的處理方式,一個(gè)一個(gè)的構(gòu)造;如果是內(nèi)置類型,這樣顯然太低效,使用memcpy()可能會(huì)好一些