C++之函數(shù)模板
為什么需要模板
相信寫過Java的童鞋們都知道泛型編程,在C++中與之對應(yīng)的就是模板。
模板是一種對類型進行參數(shù)化的工具,通常有兩種形式:函數(shù)模板和類模板。
模板是一些為多種類型而編寫的函數(shù)和類,而且這些類型都沒有指定。當(dāng)使用模板的時候,你只需要把所希望的類型作為一個(顯式或者隱式的)實參傳遞給模板。 另外,由于模板是語言本身所具有的特性,所以它完全支持類型檢查和作用域。使用模板的目的就是能夠讓程序員編寫與類型無關(guān)的代碼,盡可能地減少重復(fù)代碼。
眾所周知,C++是一門強類型的靜態(tài)語言。在聲明變量、函數(shù)和大多數(shù)其他類型的實體的時候,C++要求我們使用指定的類型。 然而,對于許多代碼,除了類型不同之外,其余的代碼看起來都是相同的。例如我們需要實現(xiàn)一個交換兩個變量的函數(shù),為了通用性, 這個交換變量的函數(shù)不能固定兩個變量的類型,這就使得模版橫空出世。。。
在C++標(biāo)準(zhǔn)庫中,幾乎所有的代碼都是模板代碼,可以說沒有C++模板就沒有STL。
模板函數(shù)
首先我們看下函數(shù)模板的格式:
template <typename 形參名,typename 形參名,......>
返回類型 函數(shù)名(參數(shù)列表)
{
函數(shù)體
}
或者使用class關(guān)鍵字也可:
template <class 形參名,class 形參名,......>
返回類型 函數(shù)名(參數(shù)列表)
{
函數(shù)體
}
為什么會有兩種不同的格式呢?這是因為鑒于歷史的原因,你可能還會使用class取代typename,來定義類型參數(shù)。 在C++語言的演化過程中,關(guān)鍵字typename的出現(xiàn)相對較晚一些;在它之前,關(guān)鍵字class是引入類型參數(shù)的唯一方式,并一直作為有效方式保留下來。 但是更加標(biāo)準(zhǔn)的格式是使用typename關(guān)鍵字。
例如我們使用模板定義了返回較大值的模板函數(shù):
template <typename T>
const T& max_fun(const T& a,const T& b){
return a >= b? a:b;
}
下面我們調(diào)用一下我們定義的模板函數(shù)max_fun:
int main(int argc, char* argv[]) {
// 都是int類型 ok
int max = max_fun(10,10);
// 都是double類型 ok
int max = max_fun(10,10);
// 一個int類型,一個double類型 編譯不通過
int max = max_fun(10,11.0);
std::cout << "max:" << max << std::endl;
return 0;
}
在上面的例子中我們發(fā)現(xiàn)函數(shù)max_fun(10,11.0)報錯了,無法編譯通過,這是為什么呢?因為我們定義的模板函數(shù)max_fun只有一個參數(shù)類型, 但是max_fun(10,11.0)卻傳了兩個不同的參數(shù)類型,二載函數(shù)模板中是不允許進行自動類型轉(zhuǎn)換的,因此報錯,有兩種方式可以解決這個報錯:
- 一是對實參進行強制類型轉(zhuǎn)換,使它們可以互相匹配:
max_fun(10,static_cast<int>(11.0));
- 二是顯式指定(或者限定)T的類型:
max_fun<int>(10,11.0);
重點:在函數(shù)模板實參演繹的過程中,是不允許進行自動類型轉(zhuǎn)換的。
重載函數(shù)模板
模板函數(shù)在使用時編譯器回自動實現(xiàn)實例化,只要使用函數(shù)模板,(編譯器)會自動地引發(fā)這樣一個實例化過程,因此程序員并不需要額外地請求模板的實例化。
和普通函數(shù)一樣,函數(shù)模板也可以被重載。就是說,相同的函數(shù)名稱可以根據(jù)不同的函數(shù)參數(shù)具有不同的函數(shù)定義; 于是,當(dāng)使用函數(shù)名稱進行函數(shù)調(diào)用的時候,C++編譯器必須決定究竟要調(diào)用哪個候選函數(shù)。
一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,而且該函數(shù)模板還可以被實例化為這個非模板函數(shù)。 對于非模板函數(shù)和同名的函數(shù)模板,如果其他條件都是相同的話,那么在調(diào)用的時候,重載解析過程通常會調(diào)用非模板函數(shù),而不會從該模板產(chǎn)生出一個實例。
下面我們通過一個小例子來了解下重載函數(shù)模板:
#include <iostream>
#include <memory>
template <typename T>
const T& max_fun(const T& a,const T& b){
std::cout << "模板類型max_fun:"<< std::endl;
return a >= b? a:b;
}
const int& max_fun(const int& a,const int& b){
std::cout << "int 類型max_fun:"<< std::endl;
return a >= b? a:b;
}
int main(int argc, char* argv[]) {
// 都是double類型 匹配到模板函數(shù)max_fun
// int max = max_fun(10.0,11.0);
// 都是int類型 匹配到 普通函數(shù)max_fun
// int max = max_fun(10,10);
// 一個char類型,一個double類型 匹配到普通函數(shù)max_fun
int max = max_fun('a',11.0);
std::cout << "max:" << max << std::endl;
return 0;
}
在上面的例子main函數(shù)中我們多次調(diào)用了函數(shù)max_fun,那么怎么區(qū)分是調(diào)用了模板函數(shù)max_fun還是調(diào)用了重載的普通函數(shù)max_fun呢?
有一條規(guī)則是這樣的:
一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,而且該函數(shù)模板還可以被實例化為這個非模板函數(shù)。對于非模板函數(shù)和同名的函數(shù)模板,如果其他條件都是相同的話,那么在調(diào)用的時候,重載解析過程通常會調(diào)用非模板函數(shù),而不會從該模板產(chǎn)生出一個實例。
因此在上面的例子中我們可以很容易地看出第17行調(diào)用的是模板函數(shù)max_fun,因為沒有參數(shù)是double類型的max_fun被重載。 但在第19行因為有一個參數(shù)是int類型的重載函數(shù)max_fun,因此這一行調(diào)用的是普通重載函數(shù)max_fun。
那么在第21行也是調(diào)用了int類型的重載函數(shù)max_fun,這是為什么呢?
這是因為模板是不允許自動類型轉(zhuǎn)化的,但普通函數(shù)可以進行自動類型轉(zhuǎn)換,所以第21行調(diào)用的是int類型的重載函數(shù)max_fun(‘a(chǎn)’和11.0都被轉(zhuǎn)化為int)。
函數(shù)重載應(yīng)該牢記一條首要規(guī)則:函數(shù)的所有重載版本的聲明都應(yīng)該位于該函數(shù)被調(diào)用的位置之前。