一:廢話html
今天在stackoverflow上看到一個關於c++模板specialization的問題:linux
他的English好像不是很標準(說不定是India三哥,哈哈),但比我強多了。廢話很少說,問題簡述以下:app
//#1 template<class X> void foo(X a) { cout << "Template 1" << endl; } //#2 template<class X> void foo(X *a) { cout << "Template 2" << endl; }
template<> void foo<>(int *a) { cout << "Specialization 1" << endl; }
那麼這哥們的問題以下:less
1 這個函數是屬於template #1呢仍是屬於template #2呢?jsp
2 若是這個特例化定義在template #2以前和以後,結論會有差異嗎?函數
其實看過c++模板的內容,但沒怎麼用過複雜的模板,只是簡單的寫一些模板函數,方便適應不一樣的參數。 因此對模板特例化沒有特別深刻了解, 對偏序機制也就沒有什麼概念。正巧遇到這個哥們問了這樣一個問題,我試着去回答,可是無能爲力,正好有一位大神幫他回答了,因而我也順便請教了這位大神,他說這個偏序化機制在模板中是一個比較複雜的概念,涉及內容比較多。瞭解了這個之後,我Google了一點資料,寫下這篇當心得。ui
二: 偏序化(Partial Ordering)spa
應該是這麼翻譯吧!先看下什麼叫partial ordering?引用參考資料1裏面的介紹:nuxt
A function template specialization might be ambiguous because template argument deduction might associate the specialization with
more than one of the overloaded definitions. The compiler will then choose the definition that is the most specialized. This process of selecting a function template definition is called partial ordering
三: 介紹
在介紹以前,先看看什麼叫最特例化?舉個例子:
//#1 template<class T> void f(T); //#2 template<class T> void f(T*); //#3 template<class T> void f(const T*);
上述三個模板中,特例化的程度從大到小依次爲:
#3 > #2 > #1
若是如今有這樣一個調用:
int *p = NULL; f(p);
那麼編譯器確定會選擇#2模板,而不是#1模板,由於#2模板比#1模板更特例化。爲何不選#3模板?由於還有一個規則,優先選擇類型顯式匹配的模板,若是調用#3號模板,須要隱式轉換。
而後,接下來的問題是:編譯器怎麼知道#2模板比#1模板更特例化?下面就是我要說的partial ordering。編譯器經過以下的方法來判斷:
1 先選擇兩個函數模板,T1和T2
2 用假設的惟一類型X取代模板T1的參數
3 用被X取代後的T1的參數列表,帶入T2,看T2是不是一個有效的模板。忽略全部的隱式轉換。
4 反過來,先用X取代T2的參數,再把T2的參數列表帶入T1,看看T1是否有效。
5 若是一個模板的參數好比T1對於另一個模板T2是有效的,可是反之不成立,那麼就說這個模板T1不比T2更特例化。若是這兩個模板的參數均可以相互代替,就說它們具備相同的特例性,這樣會引發編譯器混淆。
template<class T> void g(T) { } template<class T> void g(T&) { }
template<class T> void h(T) { } template<class T> void h(T, ...) { } //error C2668: 'h' : ambiguous call to overloaded function
可變參數不會引發編譯器執行partial ordering規則,因此這兩種模板也會引發歧義。
四:使用
partial ordering的判斷實例:
1 對於一個模板,特定類型的參數比通常類型的參數,更具備特例性
2 帶有T*的模板比T的模板具備特例性。由於一個假設的類型X*也能夠被認爲是T類型的, 相反一個有效的T類型參數,可能不是X*類型的。
3 const T比T更特例化,道理同上。
4 const T*比const T更特例化,理由也是同樣的。
template <class T> void f(T) { cout<<"f(T):Less specialized function called"<<endl; } template <class T> void f(T*) { cout<<"f(T*):More specialized function called"<<endl; } template <class T> void f(const T*) { cout<<"f(const T*):Even more specialized function for const T*"<<endl; } int _tmain(int argc, _TCHAR* argv[]) { int i =0; const int j = 0; int *pi = &i; const int *cpi = &j; f(i); // Calls less specialized function. f(pi); // Calls more specialized function. f(cpi); // Calls even more specialized function. // Without partial ordering, these calls would be ambiguous. }
什麼狀況下,編譯器會執行這樣的一個Partial Ordering?文獻1給出了幾種狀況:
· Calling a function template specialization that requires overload resolution.
· Taking the address of a function template specialization.
· When a friend function declaration, an explicit instantiation, or explicit specialization refers to a function template specialization.
· Determining the appropriate deallocation function that is also a function template for a given placement operator new.
(2) 獲取函數模板特例的地址
(3) 當一個友元函數聲明,或者顯示實例化,或者引用函數模板的顯示特例化
(4) 對一個new出來的內存進行銷燬時(這個new函數也是模板函數),如何選擇相應的釋放函數也會引起partial ordering。
REFERENCE: