c++模板函數實例化的偏序機制


一:廢話

  今天在stackoverflow上看到一個關於c++模板specialization的問題:

http://stackoverflow.com/questions/18283851/template-specialization-in-case-of-multiple-base-templates-in-c/18283933?noredirect=1#comment26823443_18283933

他的English好像不是很標准(說不定是India三哥,哈哈),但比我強多了。廢話不多說,問題簡述如下:

//#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;
}

那么這哥們的問題如下:

這個函數是屬於template #1呢還是屬於template #2呢?

如果這個特例化定義在template #2之前和之后,結論會有差別嗎?

  其實看過c++模板的內容,但沒怎么用過復雜的模板,只是簡單的寫一些模板函數,方便適應不同的參數。 所以對模板特例化沒有特別深入了解, 對偏序機制也就沒有什么概念。正巧遇到這個哥們問了這樣一個問題,我試着去回答,但是無能為力,正好有一位大神幫他回答了,於是我也順便請教了這位大神,他說這個偏序化機制在模板中是一個比較復雜的概念,涉及內容比較多。了解了這個以后,我Google了一點資料,寫下這篇小心得。

 

二: 偏序化(Partial Ordering)

應該是這么翻譯吧!先看下什么叫partial ordering?引用參考資料1里面的介紹:

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更特例化。如果這兩個模板的參數都可以相互代替,就說它們具有相同的特例性,這樣會引起編譯器混淆。
  舉兩個會引起混淆的一個例子,比如:
(1)
template<class T> void g(T) { }

template<class T> void g(T&) { }
這兩個模板的參數可以相互替代,所以編譯器會報錯。
 
(2)
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.
(1) 調用函數模板特例時,涉及到重載決議

(2) 獲取函數模板特例的地址

(3) 當一個友元函數聲明,或者顯示實例化,或者引用函數模板的顯示特例化

(4) 對一個new出來的內存進行銷毀時(這個new函數也是模板函數),如何選擇相應的釋放函數也會引發partial ordering

 

 

REFERENCE:

http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.vacpp7a.doc%2Flanguage%2Fref%2Fclrc16part_ord_fn_tmpl.htm

http://msdn.microsoft.com/en-us/library/zaycz069.aspx

http://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_117.html

http://stackoverflow.com/questions/17005985/whats-the-partial-ordering-procedure-in-template-deduction

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM