我們很難寫出所有可能被實例化的類型都合適的模板。某些情況下,
通用模板定義對于某個類型可能是完全錯誤的,所以我們需要能夠?qū)崿F(xiàn)處
理某些特殊情況,特化的概念變是如此。compare函數(shù)和Queue類是這
個問題的很好例子。因為與C風(fēng)格字符串一起使用時,他們都不能正確工作
。
template <typename T>
int compare(const T &v1,const T &v2)
{
if(v1 < v2) return -1;
if(v2 < v1) return 1;
return 0;
}
如果用兩個const char* 實參調(diào)用這個模板定義,函數(shù)將比較指針的
值。也就是比較兩個指針在內(nèi)存中的相對位置,卻并沒有說明與指針?biāo)?br> 數(shù)組的內(nèi)容有關(guān)的任何事情。
為了能夠?qū)ompare函數(shù)用于字符串,必須提供一個知道怎樣比較C風(fēng)
格字符串的特殊定義。這些就被稱作是特化的,它對模板的用戶而言是透
明的。
1. 函數(shù)模板的特化
特化形式:
- 關(guān)鍵字template后面接一對空的尖括號<>;
- 再接模板名和一對尖括號<>,尖括號中指定這個特化定義的模板參數(shù):
- 函數(shù)形參表
- 函數(shù)體
template<>
int compare<const char*> (const char* const &v1,
const char* const &v2)
{
return strcmp(v1,v2);
}
特化的聲明必須與 對應(yīng)的模板相匹配。類型形參固定為const char*。
因此,函數(shù)形參是const char* 的const引用。當(dāng)調(diào)用compare函數(shù)的時候,
傳給它兩個字符指針,編譯器將調(diào)用特化版本。而不調(diào)用上面的泛型版本。
const char *cp1 = "world", *cp2 = "hi";
int i1, i2;
compare(cp1, cp2); //調(diào)用特化函數(shù)模板
compare(i1, i2); //調(diào)用泛型函數(shù)模板
注意:
* 函數(shù)模板特化時template<>不能省略,如果缺少結(jié)果是聲明該函數(shù)的重載
。
* 必須包含函數(shù)形參列表。如果可以從形參列表推斷模板實參,則不必顯示
指
定模板實參。
* 如果程序由多個文件構(gòu)成,模板特化的聲明必須在使用該特化的每個文件
中出現(xiàn)。
2.類模板的特化
當(dāng)使用C風(fēng)格字符串時,Queue類具有 compare函數(shù)相似的問題。問題就處
在push函數(shù)中,該函數(shù)復(fù)制給定的值以創(chuàng)建Queue中的新元素。默認(rèn)情況下
,
復(fù)制C風(fēng)格字符串只會復(fù)制指針,不會復(fù)制字符。而顯然復(fù)制指針將出現(xiàn)一
系
列的嚴(yán)重問題。為了解決復(fù)制C風(fēng)格字符串的問題,需要為const char*定義
整個類的特化版本:
template<> class Queue<const char*>{
public:
void push(const char*);
void pop() {real_queue.pop();}
bool empty() const {return real_queue.front();}
//返回類型與模板參數(shù)類型不同
std::string front() {return real_queue.front();}
const std::string &front() const {return real_queue.front();
private :
Queue<std::string> real_queue;
};
給Queue一個新的數(shù)據(jù)元素,string對象的Queue。
在類的外部定義一個成員:
void Queue<const char*>::push (const char* val)
{
return real_queue.push(val);
}
這個函數(shù)通過調(diào)用read_queue的push函數(shù)把val指向的數(shù)組復(fù)制到未命名的
string 對象中。當(dāng)需要出隊列的時候調(diào)用相應(yīng)real_queue.pop()函數(shù)即返
回了這個string,從而解決了不用復(fù)制指針的問題。
3.特化成員而不特化類
在上例的實現(xiàn)中,我們可以換一種方法,即不需要特化類,而只需要特化類
的成員函數(shù)push、pop。
根據(jù)函數(shù)模板特化的要求:
template <>
void Queue<const char*>::push(const char *const &val)
{
char * new_item = new char[strlen(val)+1];
strncpy(new_item, val, strlen(val)+1);
QueueItem<const char*> *pt =
new QueueItem<const char*>(new_item);
if(empty())
head = tail = pt; //隊列中沒有元素
eles{
tail->next = pt; //添加新元素到列尾
tail = pt;
}
}
template<>
void Queue<const char*>::pop()
{
QueueItem<const char*> *p = head;
delete head->item; //刪除隊首元素
head = head->next; //指向當(dāng)前隊首元素
delete p; //刪除零時指針
}
4.類模板的部分特化
如果類模板有一個以上的模板形參,我們很有可能只要特化某些模板形參
而不是全部形參。這時我們就需要使用類的部分特化。
//定義模板類
template <class T1, class T2>
class some_template{
// ...
};
//定義模板類的部分特化:T2類型固定,部分特化T1類型
template<class T1>
class some_template<T1, int>{
// ...
};
//使用類模板的部分特化
some_template<int, string> foo; //使用模板類
some_template<string,int> bar; //使用模板類的部分特化
通過使用模板特化能解決一些在通常或者通用情況下無法解決的特殊問題。
在掌握了基本的語法規(guī)范和實現(xiàn)方法后便可以加以應(yīng)用。