C++模板专门化与重载
最近在復(fù)習(xí)C++有關(guān)知識,又重新看<<Effective C++>>,收獲頗豐。原來以前看這邊書,好多地方都是淺嘗輒止。<<Effective C++>>條款25:考慮寫出一個不拋出異常的swap函數(shù),涉及到C++模板專門化(Templates?Specialization)和函數(shù)重載(overloading)問題,而當(dāng)重載與模板攪合在一起時,許多問題都變得“模棱兩可”。
首先回顧<<Effective C++>>條款25:考慮寫出一個不拋出異常的swap函數(shù),想告訴我們什么東西。
swap函數(shù)為我們提供了異常安全編程的方法,以及用來作為處理自我賦值一種常見機(jī)制,因此實(shí)現(xiàn)一個不拋出異常的swap函數(shù),變得相對重要起來。缺省情況下的swap函數(shù)的典型實(shí)現(xiàn)如下:
namespace std {template<typename T>void swap(T& a, T& b){T temp(a);a = b;b = temp; } }然后,對于模型數(shù)據(jù)類型其成員變量是指針,指向一個對象,保存了數(shù)據(jù)(pointer to implementation手法)。如果copying函數(shù)采用了deep copying方法,上面的代碼將會非常低效,因?yàn)?#xff0c;只需要互換a與b指針即可。問題是,缺省版本swap對類型無法可知這些信息,因此針對上述情況,需要專門化swap函數(shù)。
1)如果T是class,可以先讓T提供一個swap函數(shù),完成swap功能,然后借由functhon template的全特化,實(shí)現(xiàn)專門化的swap函數(shù):
class Widge { public:void swap(Wiget& other){using std::swap();swap(pImpl, other.pImpl);}private: WidetImpl* pImpl;
};
//為程序提供一個特化版本的swap: namespace std
{
template<>
void swap<Widegt>(Widget& a, Widget& b)
{ a.swap(b);
}
}
上面的代碼很好的與STL容器保持了一致性,因?yàn)镾TL容器也都提供了swap成員函數(shù)和std::swap特化版本。
2)如果Widget與WidgetImpl不是class,而是class template,特化版本的swap函數(shù),我們可能想寫成這樣的形式:
namespace std {template<class T>void swap<Widegt<T>>(Widget<T>& a, Widget<T>& b) {a.swap(b);} }然而這個代碼卻無法通過編譯,C++不支持function template的偏特化,我們需要使用模板函數(shù)的重載技術(shù):
namespace std {template<class T>void swap( Widget<T>& a, Widget<T>& b) //重載了function templates {a.swap(b);} }問題似乎已經(jīng)解決了,嗯,是的,還存在一個問題:用戶可以全特化std內(nèi)的templates,但是不能新的對象(template、function、class)。解決方法是將這些類與swap函數(shù)放到新的命名空間中,這邊便獨(dú)立與std命名空間。
--------------------------------------------------------------------華麗分割線--------------------------------------------------------------------
上面介紹的內(nèi)容,涉及到以下的內(nèi)容:1)模板函數(shù);2)重載函數(shù);3)全特化和偏特化。當(dāng)這些東西交織在一起的時候,我們需要足夠的耐心做區(qū)分甄別。
1)模板類、模板函數(shù)與重載
// Example 1: Class vs. function template, and overloading //// A class templatetemplate<typename T> class X { /*...*/ }; // (a) 類模板// A function template with two overloadstemplate<typename T> void f( T ); // (b) 函數(shù)模板 template<typename T> void f( int, T, double ); // (c) 函數(shù)模板重載(a)、(b)、(c)均沒有專門化,這些未被專門化的template又被稱為基礎(chǔ)基模板。
2)特化
template class可以有全特化與偏特化兩種, template function僅能全特化。
// Example 1, continued: Specializing templates // // A partial specialization of (a) for pointer types template<typename T> class X<T*> { /*...*/ }; // A full specialization of (a) for int template<> class X<int> { /*...*/ };// A separate base template that overloads (b) and (c) // -- NOT a partial specialization of (b), because // there's no such thing as a partial specialization // of a function template! template<class T> void f( T* ); // (d)// A full specialization of (b) for int template<> void f<int>( int ); // (e)// A plain old function that happens to overload with // (b), (c), and (d) -- but not (e), which we'll // discuss in a moment void f( double ); // (f)當(dāng)function template與重載攪合在一起的時候,就存在匹配哪個版本函數(shù)的問題,匹配規(guī)則如下:
1)首先查找non template function ,如果在這些函數(shù)中匹配成功,則匹配結(jié)束(first-class citizens)
2)否定,在base template function 中查找最匹配的函數(shù),并實(shí)例化,如果base template function恰巧有提供全特化版本模板函數(shù),則使用全特化版本(sencond-class citizens)
?將以上兩個規(guī)則運(yùn)用的例子:
// Example 1, continued: Overload resolution // bool b; int i; double d;f( b ); // calls (b) with T = bool f( i, 42, d ); // calls (c) with T = int f( &i ); // calls (d) with T = int f( i ); // calls (e) f( d ); // calls (f) 最后一個問題:如何判斷哪個base template function被specialization,再看下面的例子:
c是b是全特化,f(p)將會調(diào)用,符合人們的一般想法,但是,如果置換b與c的順序,結(jié)果就不那么一樣了:
// Example 3: The Dimov/Abrahams Example // template<class T> // (a) same old base template as before void f( T );template<> // (c) explicit specialization, this time of (a) void f<>(int*);template<class T> // (b) a second base template, overloads (a) void f( T* );// ...int *p; f( p ); // calls (b)! overload resolution ignores // specializations and operates on the base // function templates only這個時候,c將是a的全特化(編譯器沒看到后面的b的定義)。按照配對規(guī)則,首先查找base template function最適合匹配的,b正好最為匹配,并且沒有全特化版本,因此將會調(diào)用b。
重要準(zhǔn)則:
1)如果我們希望客戶化base template function,直接利用傳統(tǒng)的函數(shù)形式,如果使用重載形式,那么請不要提供全特化版本。
2)如果正在編寫一個base template function,不要提供特化和重載版本,將客戶化定制功能下放給用戶。實(shí)現(xiàn)方法是,在class template 同static 函數(shù)接口:
// Example 4: Illustrating Moral #2 // template<class T> struct FImpl;template<class T> void f( T t ) { FImpl<T>::f( t ); } // users, don't touch this! template<class T> struct FImpl { static void f( T t ); // users, go ahead and specialize this };準(zhǔn)則2的動機(jī)就是利用class template 特化與偏特化功能實(shí)現(xiàn)function 特化與偏特化功能。
?
參考文獻(xiàn):
<<Effective C++>>, Scott Meyers
<<Why Not Specialize Function Templates?>>,??C/C++ Users Journal, 19(7), July 2001
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
轉(zhuǎn)載于:https://www.cnblogs.com/wangbogong/p/3249238.html
總結(jié)
以上是生活随笔為你收集整理的C++模板专门化与重载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络安全基础之十八
- 下一篇: 【C++ 学习笔记】 MFC CEdit