C++模板的特化
特化整體上分為全特化和偏特化,這一點(diǎn)大家都沒(méi)有什么置疑,但是細(xì)分它們各包括哪幾種狀態(tài)就很難界定了,而且很多權(quán)威的書上都不一致,管它呢,反正我們能會(huì)用各種特化就可以了。
下面就談?wù)勎覀€(gè)人對(duì)特化的劃分和定義:
所謂特化,就是將泛型的東東搞得具體化一些,從字面上來(lái)解釋,就是為已有的模板參數(shù)進(jìn)行一些使其特殊化的指定,使得以前不受任何約束的模板參數(shù),或受到特定的修飾(例如const或者搖身一變成為了指針之類的東東,甚至是經(jīng)過(guò)別的模板類包裝之后的模板類型)或完全被指定了下來(lái)。
這是網(wǎng)上某個(gè)人的一些看法:
模板有兩種特化,全特化和偏特化(局部特化)
模板函數(shù)只能全特化,沒(méi)有偏特化(以后可能有)。
模板類是可以全特化和偏特化的。
全特化,就是模板中模板參數(shù)全被指定為確定的類型。
全特化也就是定義了一個(gè)全新的類型,全特化的類中的函數(shù)可以與模板類不一樣。
偏特化,就是模板中的模板參數(shù)沒(méi)有被全部確定,需要編譯器在編譯時(shí)進(jìn)行確定。
在類型上加上const、&、*( cosnt int、int&、int*、等等)并沒(méi)有產(chǎn)生新的類型。只是類型被修飾了。模板在編譯時(shí),可以得到這些修飾信息。
我個(gè)人也比較贊同這位仁兄的劃分,全特化的標(biāo)志就是產(chǎn)生出完全確定的東西,而不是還需要在編譯期間去搜尋適合的特化實(shí)現(xiàn),貌似在我的這種理解下,全特化的東西不論是類還是函數(shù)都有這樣的特點(diǎn),template <>然后是完全和模板類型沒(méi)有一點(diǎn)關(guān)系的類實(shí)現(xiàn)或者函數(shù)定義,如果你要說(shuō),都完全確定下來(lái)了,那還搞什么模板呀,直接定義不就完事了?但是很多時(shí)候,我們既需要一個(gè)模板能應(yīng)對(duì)各種情形,又需要它對(duì)于某個(gè)特定的類型(比如bool)有著特別的處理,這中情形下就是需要的了。
既然剛才提到了全特化的標(biāo)志,那么再說(shuō)說(shuō)其他一些共性的東西:
一個(gè)特化的模板類的標(biāo)志:在定義類實(shí)現(xiàn)時(shí)加上了<>,比如class A<int, T>;而在定義一個(gè)模板類的時(shí)候,class A后面是沒(méi)有<>的
全特化的標(biāo)志:template <>然后是完全和模板類型沒(méi)有一點(diǎn)關(guān)系的類實(shí)現(xiàn)或者函數(shù)定義
偏特化的標(biāo)志:template <typename T.....>,就是說(shuō)還剩下點(diǎn)東西,不像全特化<>整得那么徹底
首先推薦兩個(gè)不錯(cuò)的網(wǎng)址:
http://www.cnblogs.com/cutepig/archive/2009/02/12/1389479.html
http://read.newbooks.com.cn/info/175115.html
先說(shuō)類模板的特化吧:
誰(shuí)都沒(méi)的說(shuō)的全特化:
// general version
template<class T>
class Compare
{
public:
static bool IsEqual(const T& lh, const T& rh)
{
return lh == rh;
}
};
// specialize for float
template<>
class Compare<float>
{
public:
static bool IsEqual(const float& lh, const float& rh)
{
return abs(lh - rh) < 10e-3;
}
};
誰(shuí)都沒(méi)的說(shuō)的偏特化:
template<class T1, class T2>
class A
{
}
template<class T1>
class A<T1, int>
{
}
接下來(lái)的特化種類,到底劃歸到全特化還是偏特化,你自己看著辦吧,不過(guò)大致就以下這些了,逃不出我們的手掌心了:
特化為引用,指針類型:
// specialize for T*
template<class T>
class Compare<T*>
{
public:
static bool IsEqual(const T* lh, const T* rh)
{
return Compare<T>::IsEqual(*lh, *rh);
}
};
特化為另外一個(gè)類模板:
// specialize for vector<T>
template<class T>
class Compare<vector<T> >
{
public:
static bool IsEqual(const vector<T>& lh, const vector<T>& rh)
{
if(lh.size() != rh.size()) return false;
else
{
for(int i = 0; i < lh.size(); ++i)
{
if(lh[i] != rh[i]) return false;
}
}
return true;
}
};
混合型的:
template<typename T1, typename T2>
class X {...};
template<typename T>
class X<vector<T>, int&> {...}; //至于這里怎么都把T2搞沒(méi)了變成只依賴一個(gè)模板參數(shù)T了的問(wèn)題,大家別著急,我來(lái)告訴你個(gè)本質(zhì)的東西,把我這么三點(diǎn)就可以了:1.模板參數(shù)個(gè)數(shù)一致;2.只要template <...>里面有東西不是<>,比如typename T,那么特化時(shí)就得用到T;3.不進(jìn)行任何對(duì)模板參數(shù)的修飾也是不行的,比如template<typename T> class<T>{...},至少你也得搞個(gè)const T之類的吧,呵呵。下面是我搞出來(lái)的幾種特殊情況,它們都是正確的:
template<typename T1, typename T2>
class X {};
template<typename T>
class X<vector<T>, T&> {};
template<typename T>
class X<vector<T>, int&> {};
template<>
class X<vector<double>, int&> {};
template<typename T1, typename T2, typename T3>
class X<map<T1,T2>, T3&> {};
最后,還有一種超級(jí)牛X的,在tr1里面用以實(shí)現(xiàn)function的,以前我都沒(méi)見過(guò)還可以這么玩的:
template<typename T>
class Y;//這是在聲明一個(gè)類模板,既然聲明了,以后就得按這個(gè)規(guī)矩來(lái),在我們之前的編程經(jīng)驗(yàn)里,可以重復(fù)聲明一個(gè)東西沒(méi)問(wèn)題,但是為同一個(gè)東東重復(fù)聲明出不同的東西就不可以了,因此你就不能再聲明諸如template<typename T1, typename T2> class Y;這樣的聲明了;其實(shí)沒(méi)有什么是不能聲明的,既然我們可以聲明變量,聲明函數(shù),聲明類,那么當(dāng)然我們也可以聲明函數(shù)模板或者類模板的。
template<typename R, typename P1, typename P2>
class Y<R (P1, P2)> {...};//針對(duì)帶兩個(gè)參數(shù),有返回值的函數(shù)類型特化,這里R (P1,P2)是定義了一種類型,該類型是一個(gè)隱式的函數(shù)指針,返回R,參數(shù)為P1和P2,這種對(duì)函數(shù)指針的定義完全等同于R (*)(P1,P2),但是前一種定義很不常見,大家一般是不會(huì)注意到這個(gè)地方的。
好了,說(shuō)了不少關(guān)于類模板的特化了,下面再簡(jiǎn)要說(shuō)說(shuō)函數(shù)模板的特化:
函數(shù)模板的特化只能是全特化,而不能是偏特化,因此對(duì)于函數(shù)的特化就比較簡(jiǎn)單了,就是重新搞一遍就可以了,舉幾個(gè)例子如下:
template <class T>
T mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
template <>
const char* mymax(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0) ? t2 : t1;
}
但是你不能這么搞:
template <>
bool mymax(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0);
}
其實(shí)對(duì)于mymax這個(gè)模板函數(shù)的定義而言,是用一個(gè)模板參數(shù)控制了三個(gè)地方,那么你在特化的時(shí)候,就也需要用一個(gè)特定的類型修改那三處相應(yīng)的地方,如果你非要返回bool,那么你只能再定義一個(gè)函數(shù)模板了:
template <class T>
bool mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
問(wèn)題又來(lái)了,大家都知道函數(shù)重載是不關(guān)心返回值的,而只關(guān)心參數(shù)個(gè)數(shù)以及類型是否不一致,不一致就是重載,但是對(duì)于模板函數(shù)而言,這個(gè)規(guī)矩不再成立,因?yàn)槿魏闻c模板相關(guān)的東西都只是個(gè)架子放在那里而已,只要它符合語(yǔ)法規(guī)則就可以了,這些架子只是在有人要調(diào)用它們時(shí)才會(huì)發(fā)揮效力,也就是說(shuō),在編譯的時(shí)候會(huì)為你搜尋合適的模板函數(shù)或者類,只要能找到就ok了,而且還要求是只找到一個(gè),要是找到多個(gè)也不行,呵呵。
其實(shí),對(duì)于函數(shù)而言,雖然不能偏特化,即不能再在函數(shù)名字后面像模板類一樣搞個(gè)<typename T>出來(lái),但是可以通過(guò)函數(shù)的重載(注意這里說(shuō)的重載是指的模板重載,而不是普通意義的函數(shù)重載)變通的實(shí)現(xiàn)偏特化:
template <typename T1, typename T2>
bool mymax(T1 t1, T2 t2)
{
return t1 < t2 ? t2 : t1;
}
template <typename T1>
bool mymax(T1 t1, int t2)
{
return t1 < t2 ? t2 : t1;
}
再談?wù)労瘮?shù)模板參數(shù)的推導(dǎo),大致有以下幾種方法,但是不管怎么推導(dǎo),都必須得保證在調(diào)用函數(shù)前能確定模板函數(shù)的各個(gè)模板參數(shù)的類型。
template <typename T1, typename T2>
T2 fun(T1 arg1, int arg2)
{
T2 t2;
return t2;
}
對(duì)于上面這種比較特殊的模板函數(shù),你不能通過(guò)傳遞參數(shù)來(lái)自動(dòng)得到所有模板參數(shù)的類型,因此你必須顯示的指定T1和T2的類型,有兩種方法可以實(shí)現(xiàn)此目的:
int (*pfun)(double,int) = fun;//借用函數(shù)指針定義
cout<<pfun(12.2,11)<<endl;
cout<<fun<int,double>(11, 3.2)<<endl;//直接指定類型
如果上述模板函數(shù)改為:
template <typename T1, typename T2>
T2 fun(T1 arg1, T2 arg2)
{
return arg2;
}
那么除了上述兩種指定模板參數(shù)類型的方法之外,由于該模板函數(shù)參數(shù)的類型都可以借由其參數(shù)獲得,因此我們省去指定模板參數(shù)這一步驟,而直接調(diào)用該模板函數(shù):
fun(23, 2.3);
最后,再談?wù)劮穷愋湍0鍏?shù)的問(wèn)題,在《C++ Template》的第四章有介紹。
template<typename T, int LEN> struct stack {...};
template<int margin> int add(int x){return x+margin;}
上面兩個(gè)例子分別對(duì)應(yīng)了類和函數(shù)兩種情形,有人說(shuō)非類型的模板參數(shù)存在得毫無(wú)價(jià)值,實(shí)則不然,因?yàn)槲覀兛梢越栌梢粋€(gè)確定的數(shù)值來(lái)產(chǎn)生一種新的類型或者新的函數(shù)。對(duì)于上面兩個(gè)例子,我覺(jué)得用非類型模板參數(shù)就很有意義,分別實(shí)現(xiàn)了讓用戶指定stack的大小以及指定需要增加的邊際值,關(guān)于更多這方面的應(yīng)用,大家可以在今后的開發(fā)過(guò)程中逐步發(fā)掘,此外,還很有必要強(qiáng)調(diào)一下對(duì)非類型模板參數(shù)的限制,不能使用浮點(diǎn)數(shù)、class類型的對(duì)象和內(nèi)部鏈接對(duì)象(例如字符串常量"hello world!")作為實(shí)參;它們可以是常整數(shù)(包括枚舉值)或者指向外部鏈接對(duì)象的指針。
對(duì)外部鏈接對(duì)象的指針舉個(gè)例子:
template <char const* name>
class MyClass {...};
extern char const s[] = ”hello”;
MyClass<s> x; //OK
好了,模板這塊內(nèi)容我先將這么多。
又從網(wǎng)上搞到點(diǎn)好東東,也貼在這里吧:
類模板:
* 如果類模板中含有靜態(tài)成員,那么用來(lái)實(shí)例化的每種類型,都會(huì)實(shí)例化這些靜態(tài)成員。
* 兩個(gè)靠在一起的模板尖括號(hào)( > ) 之間需要留個(gè)空格,否則,編譯器將會(huì)認(rèn)為是在使用operator>>,導(dǎo)致語(yǔ)法錯(cuò)誤。
* 特化的實(shí)現(xiàn)可以和基本類模板的實(shí)現(xiàn)完全不同。
* 類模板可以為模板參數(shù)定義缺省值,稱為缺省模板實(shí)參,并且他們還可以引用之前的模板參數(shù)。
* 成員函數(shù)模版不能被聲明為虛函數(shù)。
* 類模板不能和另外一個(gè)實(shí)體共享一個(gè)名稱。
eg:
2
3 class C; // ok,
4
5 int X;
6
7 template < typename T >
8
9 class X; // error. 和變量X沖突
非類型模板參數(shù):
在編譯期或鏈接期可以確定的常值。這種參數(shù)的類型必須是下面的一種:
a> 整型或枚舉
b> 指針類型( 普通對(duì)象的指針,函數(shù)指針,成員指針 )
c> 引用類型( 指向?qū)ο蠡蛘咧赶蚝瘮?shù)的引用 )
其他的類型目前都不允許作為非類型模板參數(shù)使用
今天又突然挖掘出來(lái)點(diǎn)好東東,貼在這里:
template <typename t>
void f(t t) {} //f1
template <>
void f(int t) {} //f2
void f(int t) {} //f3
void f(char t) {} //f4
f(3); //invoke f3
f('3'); //invoke f4
/**
caveat: f3 must be put after f2, or an error occurs: specialization of void f(T) [with T = int] after instantiation;
notes: the compiler will use f3 as the instantiation for f1, and use f2 as the specialization for f1;
rule: specialization must be before instantiation (*);
Above we have discuss the template function, and then we'll focus on member template function.
acronym: MTF(member template function);
Firstly, you should pay attention to the rule: the specialization of MTF must be the outside of the class, i.e., inline should not be allowed.
Secondly, specialization and instantiation still follow the rule (*). But you'd better put the instantiation outside of the class since specialization must be the outside of the class(the root cause is: if you wanna specialize a MTF, you should give compiler the defenition of the templcate firstly. But if you use the specialization as the inline method, the specialization will be anxious since you can put the defination of MTF outside of the class. As you know, the inline member function precedes the non-inline member function. So the compiler will chose the safest way to solve it, i.e., the specialization must be put outside of class declaration).
*/
posted on 2009-06-21 21:03 so true 閱讀(3838) 評(píng)論(2) 編輯 收藏 所屬分類: C&C++