C++模板的特化
對模板特化的理解:
特化整體上分為全特化和偏特化,這一點大家都沒有什么置疑,但是細分它們各包括哪幾種狀態(tài)就很難界定了,而且很多權(quán)威的書上都不一致,管它呢,反正我們能會用各種特化就可以了。
下面就談?wù)勎覀€人對特化的劃分和定義:
所謂特化,就是將泛型的東東搞得具體化一些,從字面上來解釋,就是為已有的模板參數(shù)進行一些使其特殊化的指定,使得以前不受任何約束的模板參數(shù),或受到特定的修飾(例如const或者搖身一變成為了指針之類的東東,甚至是經(jīng)過別的模板類包裝之后的模板類型)或完全被指定了下來。
這是網(wǎng)上某個人的一些看法:
模板有兩種特化,全特化和偏特化(局部特化)
模板函數(shù)只能全特化,沒有偏特化(以后可能有)。
模板類是可以全特化和偏特化的。
全特化,就是模板中模板參數(shù)全被指定為確定的類型。
全特化也就是定義了一個全新的類型,全特化的類中的函數(shù)可以與模板類不一樣。
偏特化,就是模板中的模板參數(shù)沒有被全部確定,需要編譯器在編譯時進行確定。
在類型上加上const、&、*( cosnt int、int&、int*、等等)并沒有產(chǎn)生新的類型。只是類型被修飾了。模板在編譯時,可以得到這些修飾信息。
我個人也比較贊同這位仁兄的劃分,全特化的標志就是產(chǎn)生出完全確定的東西,而不是還需要在編譯期間去搜尋適合的特化實現(xiàn),貌似在我的這種理解下,全特化的東西不論是類還是函數(shù)都有這樣的特點,template <>然后是完全和模板類型沒有一點關(guān)系的類實現(xiàn)或者函數(shù)定義,如果你要說,都完全確定下來了,那還搞什么模板呀,直接定義不就完事了?但是很多時候,我們既需要一個模板能應(yīng)對各種情形,又需要它對于某個特定的類型(比如bool)有著特別的處理,這中情形下就是需要的了。
既然剛才提到了全特化的標志,那么再說說其他一些共性的東西:
一個特化的模板類的標志:在定義類實現(xiàn)時加上了<>,比如class A<int, T>;而在定義一個模板類的時候,class A后面是沒有<>的
全特化的標志:template <>然后是完全和模板類型沒有一點關(guān)系的類實現(xiàn)或者函數(shù)定義
偏特化的標志:template <typename T.....>,就是說還剩下點東西,不像全特化<>整得那么徹底
首先推薦兩個不錯的網(wǎng)址:
http://www.cnblogs.com/cutepig/archive/2009/02/12/1389479.html
http://read.newbooks.com.cn/info/175115.html
先說類模板的特化吧:
誰都沒的說的全特化:
// 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;
}
};
誰都沒的說的偏特化:
template<class T1, class T2>
class A
{
}
template<class T1>
class A<T1, int>
{
}
接下來的特化種類,到底劃歸到全特化還是偏特化,你自己看著辦吧,不過大致就以下這些了,逃不出我們的手掌心了:
特化為引用,指針類型:
// 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);
}
};
特化為另外一個類模板:
// 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搞沒了變成只依賴一個模板參數(shù)T了的問題,大家別著急,我來告訴你個本質(zhì)的東西,把我這么三點就可以了:1.模板參數(shù)個數(shù)一致;2.只要template <...>里面有東西不是<>,比如typename T,那么特化時就得用到T;3.不進行任何對模板參數(shù)的修飾也是不行的,比如template<typename T> class<T>{...},至少你也得搞個const T之類的吧,呵呵。下面是我搞出來的幾種特殊情況,它們都是正確的:
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&> {};
最后,還有一種超級牛X的,在tr1里面用以實現(xiàn)function的,以前我都沒見過還可以這么玩的:
template<typename T>
class Y;//這是在聲明一個類模板,既然聲明了,以后就得按這個規(guī)矩來,在我們之前的編程經(jīng)驗里,可以重復聲明一個東西沒問題,但是為同一個東東重復聲明出不同的東西就不可以了,因此你就不能再聲明諸如template<typename T1, typename T2> class Y;這樣的聲明了;其實沒有什么是不能聲明的,既然我們可以聲明變量,聲明函數(shù),聲明類,那么當然我們也可以聲明函數(shù)模板或者類模板的。
template<typename R, typename P1, typename P2>
class Y<R (P1, P2)> {...};//針對帶兩個參數(shù),有返回值的函數(shù)類型特化,這里R (P1,P2)是定義了一種類型,該類型是一個隱式的函數(shù)指針,返回R,參數(shù)為P1和P2,這種對函數(shù)指針的定義完全等同于R (*)(P1,P2),但是前一種定義很不常見,大家一般是不會注意到這個地方的。
好了,說了不少關(guān)于類模板的特化了,下面再簡要說說函數(shù)模板的特化:
函數(shù)模板的特化只能是全特化,而不能是偏特化,因此對于函數(shù)的特化就比較簡單了,就是重新搞一遍就可以了,舉幾個例子如下:
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);
}
其實對于mymax這個模板函數(shù)的定義而言,是用一個模板參數(shù)控制了三個地方,那么你在特化的時候,就也需要用一個特定的類型修改那三處相應(yīng)的地方,如果你非要返回bool,那么你只能再定義一個函數(shù)模板了:
template <class T>
bool mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
問題又來了,大家都知道函數(shù)重載是不關(guān)心返回值的,而只關(guān)心參數(shù)個數(shù)以及類型是否不一致,不一致就是重載,但是對于模板函數(shù)而言,這個規(guī)矩不再成立,因為任何與模板相關(guān)的東西都只是個架子放在那里而已,只要它符合語法規(guī)則就可以了,這些架子只是在有人要調(diào)用它們時才會發(fā)揮效力,也就是說,在編譯的時候會為你搜尋合適的模板函數(shù)或者類,只要能找到就ok了,而且還要求是只找到一個,要是找到多個也不行,呵呵。
其實,對于函數(shù)而言,雖然不能偏特化,即不能再在函數(shù)名字后面像模板類一樣搞個<typename T>出來,但是可以通過函數(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ù)的推導,大致有以下幾種方法,但是不管怎么推導,都必須得保證在調(diào)用函數(shù)前能確定模板函數(shù)的各個模板參數(shù)的類型。
template <typename T1, typename T2>
T2 fun(T1 arg1, int arg2)
{
T2 t2;
return t2;
}
對于上面這種比較特殊的模板函數(shù),你不能通過傳遞參數(shù)來自動得到所有模板參數(shù)的類型,因此你必須顯示的指定T1和T2的類型,有兩種方法可以實現(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ù)的問題,在《C++ Template》的第四章有介紹。
template<typename T, int LEN> struct stack {...};
template<int margin> int add(int x){return x+margin;}
上面兩個例子分別對應(yīng)了類和函數(shù)兩種情形,有人說非類型的模板參數(shù)存在得毫無價值,實則不然,因為我們可以借由一個確定的數(shù)值來產(chǎn)生一種新的類型或者新的函數(shù)。對于上面兩個例子,我覺得用非類型模板參數(shù)就很有意義,分別實現(xiàn)了讓用戶指定stack的大小以及指定需要增加的邊際值,關(guān)于更多這方面的應(yīng)用,大家可以在今后的開發(fā)過程中逐步發(fā)掘,此外,還很有必要強調(diào)一下對非類型模板參數(shù)的限制,不能使用浮點數(shù)、class類型的對象和內(nèi)部鏈接對象(例如字符串常量"hello world!")作為實參;它們可以是常整數(shù)(包括枚舉值)或者指向外部鏈接對象的指針。
對外部鏈接對象的指針舉個例子:
好了,模板這塊內(nèi)容我先將這么多。
又從網(wǎng)上搞到點好東東,也貼在這里吧:
今天又突然挖掘出來點好東東,貼在這里:
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).
*/
特化整體上分為全特化和偏特化,這一點大家都沒有什么置疑,但是細分它們各包括哪幾種狀態(tài)就很難界定了,而且很多權(quán)威的書上都不一致,管它呢,反正我們能會用各種特化就可以了。
下面就談?wù)勎覀€人對特化的劃分和定義:
所謂特化,就是將泛型的東東搞得具體化一些,從字面上來解釋,就是為已有的模板參數(shù)進行一些使其特殊化的指定,使得以前不受任何約束的模板參數(shù),或受到特定的修飾(例如const或者搖身一變成為了指針之類的東東,甚至是經(jīng)過別的模板類包裝之后的模板類型)或完全被指定了下來。
這是網(wǎng)上某個人的一些看法:
模板有兩種特化,全特化和偏特化(局部特化)
模板函數(shù)只能全特化,沒有偏特化(以后可能有)。
模板類是可以全特化和偏特化的。
全特化,就是模板中模板參數(shù)全被指定為確定的類型。
全特化也就是定義了一個全新的類型,全特化的類中的函數(shù)可以與模板類不一樣。
偏特化,就是模板中的模板參數(shù)沒有被全部確定,需要編譯器在編譯時進行確定。
在類型上加上const、&、*( cosnt int、int&、int*、等等)并沒有產(chǎn)生新的類型。只是類型被修飾了。模板在編譯時,可以得到這些修飾信息。
我個人也比較贊同這位仁兄的劃分,全特化的標志就是產(chǎn)生出完全確定的東西,而不是還需要在編譯期間去搜尋適合的特化實現(xiàn),貌似在我的這種理解下,全特化的東西不論是類還是函數(shù)都有這樣的特點,template <>然后是完全和模板類型沒有一點關(guān)系的類實現(xiàn)或者函數(shù)定義,如果你要說,都完全確定下來了,那還搞什么模板呀,直接定義不就完事了?但是很多時候,我們既需要一個模板能應(yīng)對各種情形,又需要它對于某個特定的類型(比如bool)有著特別的處理,這中情形下就是需要的了。
既然剛才提到了全特化的標志,那么再說說其他一些共性的東西:
一個特化的模板類的標志:在定義類實現(xiàn)時加上了<>,比如class A<int, T>;而在定義一個模板類的時候,class A后面是沒有<>的
全特化的標志:template <>然后是完全和模板類型沒有一點關(guān)系的類實現(xiàn)或者函數(shù)定義
偏特化的標志:template <typename T.....>,就是說還剩下點東西,不像全特化<>整得那么徹底
首先推薦兩個不錯的網(wǎng)址:
http://www.cnblogs.com/cutepig/archive/2009/02/12/1389479.html
http://read.newbooks.com.cn/info/175115.html
先說類模板的特化吧:
誰都沒的說的全特化:
// 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;
}
};
誰都沒的說的偏特化:
template<class T1, class T2>
class A
{
}
template<class T1>
class A<T1, int>
{
}
接下來的特化種類,到底劃歸到全特化還是偏特化,你自己看著辦吧,不過大致就以下這些了,逃不出我們的手掌心了:
特化為引用,指針類型:
// 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);
}
};
特化為另外一個類模板:
// 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搞沒了變成只依賴一個模板參數(shù)T了的問題,大家別著急,我來告訴你個本質(zhì)的東西,把我這么三點就可以了:1.模板參數(shù)個數(shù)一致;2.只要template <...>里面有東西不是<>,比如typename T,那么特化時就得用到T;3.不進行任何對模板參數(shù)的修飾也是不行的,比如template<typename T> class<T>{...},至少你也得搞個const T之類的吧,呵呵。下面是我搞出來的幾種特殊情況,它們都是正確的:
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&> {};
最后,還有一種超級牛X的,在tr1里面用以實現(xiàn)function的,以前我都沒見過還可以這么玩的:
template<typename T>
class Y;//這是在聲明一個類模板,既然聲明了,以后就得按這個規(guī)矩來,在我們之前的編程經(jīng)驗里,可以重復聲明一個東西沒問題,但是為同一個東東重復聲明出不同的東西就不可以了,因此你就不能再聲明諸如template<typename T1, typename T2> class Y;這樣的聲明了;其實沒有什么是不能聲明的,既然我們可以聲明變量,聲明函數(shù),聲明類,那么當然我們也可以聲明函數(shù)模板或者類模板的。
template<typename R, typename P1, typename P2>
class Y<R (P1, P2)> {...};//針對帶兩個參數(shù),有返回值的函數(shù)類型特化,這里R (P1,P2)是定義了一種類型,該類型是一個隱式的函數(shù)指針,返回R,參數(shù)為P1和P2,這種對函數(shù)指針的定義完全等同于R (*)(P1,P2),但是前一種定義很不常見,大家一般是不會注意到這個地方的。
好了,說了不少關(guān)于類模板的特化了,下面再簡要說說函數(shù)模板的特化:
函數(shù)模板的特化只能是全特化,而不能是偏特化,因此對于函數(shù)的特化就比較簡單了,就是重新搞一遍就可以了,舉幾個例子如下:
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);
}
其實對于mymax這個模板函數(shù)的定義而言,是用一個模板參數(shù)控制了三個地方,那么你在特化的時候,就也需要用一個特定的類型修改那三處相應(yīng)的地方,如果你非要返回bool,那么你只能再定義一個函數(shù)模板了:
template <class T>
bool mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
問題又來了,大家都知道函數(shù)重載是不關(guān)心返回值的,而只關(guān)心參數(shù)個數(shù)以及類型是否不一致,不一致就是重載,但是對于模板函數(shù)而言,這個規(guī)矩不再成立,因為任何與模板相關(guān)的東西都只是個架子放在那里而已,只要它符合語法規(guī)則就可以了,這些架子只是在有人要調(diào)用它們時才會發(fā)揮效力,也就是說,在編譯的時候會為你搜尋合適的模板函數(shù)或者類,只要能找到就ok了,而且還要求是只找到一個,要是找到多個也不行,呵呵。
其實,對于函數(shù)而言,雖然不能偏特化,即不能再在函數(shù)名字后面像模板類一樣搞個<typename T>出來,但是可以通過函數(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ù)的推導,大致有以下幾種方法,但是不管怎么推導,都必須得保證在調(diào)用函數(shù)前能確定模板函數(shù)的各個模板參數(shù)的類型。
template <typename T1, typename T2>
T2 fun(T1 arg1, int arg2)
{
T2 t2;
return t2;
}
對于上面這種比較特殊的模板函數(shù),你不能通過傳遞參數(shù)來自動得到所有模板參數(shù)的類型,因此你必須顯示的指定T1和T2的類型,有兩種方法可以實現(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ù)的問題,在《C++ Template》的第四章有介紹。
template<typename T, int LEN> struct stack {...};
template<int margin> int add(int x){return x+margin;}
上面兩個例子分別對應(yīng)了類和函數(shù)兩種情形,有人說非類型的模板參數(shù)存在得毫無價值,實則不然,因為我們可以借由一個確定的數(shù)值來產(chǎn)生一種新的類型或者新的函數(shù)。對于上面兩個例子,我覺得用非類型模板參數(shù)就很有意義,分別實現(xiàn)了讓用戶指定stack的大小以及指定需要增加的邊際值,關(guān)于更多這方面的應(yīng)用,大家可以在今后的開發(fā)過程中逐步發(fā)掘,此外,還很有必要強調(diào)一下對非類型模板參數(shù)的限制,不能使用浮點數(shù)、class類型的對象和內(nèi)部鏈接對象(例如字符串常量"hello world!")作為實參;它們可以是常整數(shù)(包括枚舉值)或者指向外部鏈接對象的指針。
對外部鏈接對象的指針舉個例子:
template <char const* name>
class MyClass {...};
extern char const s[] = ”hello”;
MyClass<s> x; //OK
好了,模板這塊內(nèi)容我先將這么多。
又從網(wǎng)上搞到點好東東,也貼在這里吧:
類模板:
* 如果類模板中含有靜態(tài)成員,那么用來實例化的每種類型,都會實例化這些靜態(tài)成員。
* 兩個靠在一起的模板尖括號( > ) 之間需要留個空格,否則,編譯器將會認為是在使用operator>>,導致語法錯誤。
* 特化的實現(xiàn)可以和基本類模板的實現(xiàn)完全不同。
* 類模板可以為模板參數(shù)定義缺省值,稱為缺省模板實參,并且他們還可以引用之前的模板參數(shù)。
* 成員函數(shù)模版不能被聲明為虛函數(shù)。
* 類模板不能和另外一個實體共享一個名稱。
eg:
1 int C;
2
3 class C; // ok,
4
5 int X;
6
7 template < typename T >
8
9 class X; // error. 和變量X沖突
2
3 class C; // ok,
4
5 int X;
6
7 template < typename T >
8
9 class X; // error. 和變量X沖突
非類型模板參數(shù):
在編譯期或鏈接期可以確定的常值。這種參數(shù)的類型必須是下面的一種:
a> 整型或枚舉
b> 指針類型( 普通對象的指針,函數(shù)指針,成員指針 )
c> 引用類型( 指向?qū)ο蠡蛘咧赶蚝瘮?shù)的引用 )
其他的類型目前都不允許作為非類型模板參數(shù)使用
今天又突然挖掘出來點好東東,貼在這里:
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 閱讀(3833) 評論(2) 編輯 收藏 所屬分類: C&C++