c++模板函數
模板特化(也有翻譯為模板具體化)(specialization)
??如果把模板函數當作數學歸納法的話,模板特化就是n=常數C的情況。
//模板函數聲明
template <typename T>
bool greater(const T &a,const T &b);
int main()
{
return 0;
}
//模板函數定義
template <typename T>
bool greater(const T &a,const T &b)
{
if(a > b)
return true;
else
return false;
}
??如果上面的T是char*類型,那麽這種比較是不符合我們要求的,它比較的是地址,char*應該用strcmp。
??如果按照我們最簡單的思路的話就是直接將T換成char*,但這樣編譯卻給出了error,說這個特化找不到對應的模板函數。
#include <cstring>
//模板函數聲明
template <typename T>
bool greater(const T &a,const T &b);
//特化的模板函數的聲明
template<>
bool greater<char*>(const char * &a,const char * &b);
int main()
{
return 0;
}
//模板函數定義
template <typename T>
bool greater(const T &a,const T &b)
{
if(a > b)
return true;
else
return false;
}
//特化的模板函數的定義
template<>
bool greater<char *>(const char * &a,const char * &b)
{
return strcmp(a,b)>0?true:false;
}
??我們模板函數的定義const T &a
,其實也可以是這種寫法T const &a
,這兩種寫法表明const和T分別修飾a,也就是a的類型既是const
又是T
;而不是const T作為一個整體,a的類型是const T
。雖然在這個問題上,這兩種理解沒有出現語義的誤解,但是在上面的例子就出現誤解了。
? 根據我們原來的模板函數的定義,我們的目的應該是這樣的特化函數的參數,const (char *) &a
(是沒有這種寫法,只是為了讓我們理解方便),也就是說char *是一個整體,和const一起修飾a。const char* &a
卻讓const char
作為了一個類型,這顯然不是我們要的。char* const &a
這才是我們想要的。
#include <cstring>
//模板函數聲明
template <typename T>
bool greater(T const &a,T const &b);
//特化的模板函數的聲明
template<>
bool greater<char*>(char * const &a,char * const &b);
int main()
{
return 0;
}
//模板函數定義
template <typename T>
bool greater(T const &a,T const &b)
{
if(a > b)
return true;
else
return false;
}
//特化的模板函數的定義
template<>
bool greater<char*>(char * const &a,char * const &b)
{
return strcmp(a,b)>0?true:false;
}
??如果在寫類型時,習慣將const放在char等後面,那麽寫模板特化時,直接代換T就不會出現錯誤。
??如果在寫類型時,習慣將const放在類型char等後面,那麽寫模板特化時,直接代換T就不會出現錯誤。
定義與實現分離?
??在傳統上,我們總是把定義寫在.h文件裏,而實現文件寫在.cpp文件裏,但這在模板裏面是否可以,請看下面一個例子。
//swap.h
template <typename T>
void swap(T &a,T &b);
//swap.cpp
template <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//main.cpp
#include "swap.h"
int main()
{
int a,b;
swap(a,b);
//...
return 0;
}
??編譯器在編譯main.cpp文件時只看到swap的定義而沒有實現,所以留空,讓連接器去鏈接swap的實現。而編譯器在編譯swap.cpp時沒有看到模板參數,所以也不會編譯,最後便會導致鏈接錯誤,即找不到swap
??所以模板函數或者模板類,應該將定義和實現放在.h文件。
顯示實例化(explict initialization)
??實例化:一個通過使用具體值替換模板參數,從模板產生的普通類,函數或者成員函數的過程。
??隱式實例化:這是編譯器看到模板函數時,在當前文件實現相應的模板參數的實例化。
??顯示實例化:就是自己手工讓編譯器在此文件實現相應的模板參數的實例化。
既然編譯器會自動實現實例化,為什麽還要我們去手工去讓編譯器實現實例化呢?請看下面的例子。
//swap.h
template <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//function.h
void function();
//function.cpp
#include "swap.h"
void function()
{
int a,b;
swap(a,b);
}
//main.cpp
#include "swap.h"
#include "function.h"
int main()
{
int a,b;
swap(a,b);
function();
//...
return 0;
}
??為了容易看出,我們暫且用function表示一個用到swap的函數,它也可以是一個類。
??編譯器在編譯function.cpp時會用void swap
//swap.h
template <typename T>
void swap(T &a,T &b);
//swap
templatee <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//explict_initilization.cpp
#include "swap.cpp"
template void swap<int>(int&,int&);//顯示實例化
//function.h
void function();
//function.cpp
#include "swap.h"
void function()
{
int a,b;
swap(a,b);
}
//main.cpp
#include "swap.h"
#include "function.h"
int main()
{
int a,b;
swap(a,b);
function();
//...
return 0;
}
更加詳細的介紹可以看:c++模板類(一)理解編譯器的編譯模板過程
export的用法
??為了解決上面用一個explict_initilization.cpp來管理實例化文件的不便利,c++可以export這個關鍵詞,讓編譯器和鏈接器去管理我們上面要解決的事情。
c++模板函數