提要:《C++箴言:聲明為非成員函數的時機》闡述了為什麼只有non-memberfunctions(非成員 《C++箴言:聲明為非成員函數的時機》闡述了為什麼只有 non-member functions(非成員函數)適合於應用到所有 arguments(實參)的 implicit type conversions(隱式類型轉換),而且它還作為一個樣本使用了一個 Rational class 的 operator* function。我建議你在閱讀本文之前先熟悉那個樣本,因為本文進行了針對《C++箴言:聲明為非成員函數的時機》中的樣本做了一個無傷大雅(模板化 Rational 和 operator*)的擴充討論:
template<typename T> class Rational { public: Rational(const T& numerator = 0, // see《C++箴言:用傳引用給const取代傳值》for why params const T& denominator = 1); // are now passed by referenceconst T numerator() const; // see《C++箴言:避免返回對象內部構件的控制代碼》for why return const T denominator() const; // values are still passed by value, ... // Item 3 for why they're const }; template<typename T> const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs) { ... } |
就像在《C++箴言:聲明為非成員函數的時機》中,我想要支援 mixed-mode arithmetic(混合模式運算),所以我們要讓下面這些代碼能夠編譯。我們指望它能,因為我們使用了和 Item 24 中可以工作的代碼相同的代碼。僅有的區別是 Rational 和 operator* 現在是 templates(模板):
Rational<int> oneHalf(1, 2); // this example is from 《C++箴言:聲明為非成員函數的時機》, // except Rational is now a templateRational<int> result = oneHalf * 2; // error! won't compile |
編譯失敗的事實暗示對於模板化 Rational 來說,有某些東西和 non-template(非模板)版本不同,而且確實存在。在《C++箴言:聲明為非成員函數的時機》中,編譯器知道我們想要調用什麼函數(取得兩個 Rationals 的 operator*),但是在這裡,編譯器不知道我們想要調用哪個函數。作為替代,它們試圖斷定要從名為 operator* 的 template(模板)中執行個體化出(也就是建立)什麼函數。它們知道它們假定執行個體化出的某個名為 operator* 的函數取得兩個 Rational<T>
類型的參數,但是為了做這個執行個體化,它們必須斷定 T 是什麼。問題在於,它們做不到。
在推演 T 的嘗試中,它們會察看被傳入 operator* 的調用的 arguments(實參)的類型。在當前情況下,類型為 Rational<int>(oneHalf 的類型)和 int(2 的類型)。每一個參數被分別考察。
使用 oneHalf 的推演很簡單。operator* 的第一個 parameter(形參)被聲明為 Rational<T> 類型,而傳入 operator* 的第一個 argument(實參)(oneHalf) 是 Rational<int> 類型,所以 T 一定是 int。不幸的是,對其它參數的推演沒那麼簡單。operator* 的第二個 parameter(形參)被聲明為 Rational<T> 類型,但是傳入 operator* 的第二個 argument(實參)(2) 的 int 類型。在這種情況下,讓編譯器如何斷定
T 是什麼呢?你可能期望它們會使用 Rational<int> 的 non-eXPlicit constrUCtor(非顯式建構函式)將 2 轉換成一個 Rational<int>,這樣就使它們推演出 T 是 int,但是它們不這樣做。它們不這樣做是因為在 template argument deduction(模板實參推演)過程中從不考慮 implicit type conversion functions(隱式類型轉換函式)。從不。這樣的轉換可用於函數調用過程,這沒錯,但是在你可以調用一個函數之前,你必須知道哪個函數存在。為了知道這些,你必須為相關的
function templates(函數模板)推演出 parameter types(參數類型)(以便你可以執行個體化出合適的函數)。但是在 template argument deduction(模板實參推演)過程中不考慮經由 constructor(建構函式)調用的 implicit type conversion(隱式類型轉換)。《C++箴言:聲明為非成員函數的時機》不包括 templates(模板),所以 template argument deduction(模板實參推演)不是一個問題,現在我們在
C++ 的 template 部分,這是主要問題。
在一個 template class(模板類)中的一個 friend declaration(友元聲明)可以指涉到一個特定的函數,我們可以利用這一事實為受到 template argument deduction(模板實參推演)挑戰的編譯器解圍。這就意味著 class Rational<T> 可以為 Rational<T> 聲明作為一個 friend function(友元函數)的 operator*。class templates(類模板)不依靠 template argument deduction(模板實參推演)(這個過程僅適用於
function templates(函數模板)),所以 T 在 class Rational<T> 被執行個體化時總是已知的。通過將適當的 operator* 聲明為 Rational<T> class 的一個 friend(友元)使其變得容易
原帖地址:http://c.itwaka.com/basic1/39517.html