[原創(chuàng)文章歡迎轉載,但請保留作者信息]
Justin 于 2010-05-01
大師的課上到46堂了,今天課前要求復習一下24課的內容。
如果你懶得自己去看,這里大概提一下:24課說的是如果在實際調 用中,某個函數(shù)的任何一個參數(shù)都有可能是其他類型數(shù)據(jù)通過“類型轉換”轉換過來的,這個函數(shù)最好是寫成非成員函數(shù)。(哪 怕回去24節(jié)看,掃一遍書上的例子也知道是什么意思了)
現(xiàn)在把24課的內容升級為模板,就有了下面的代碼:
template?typename?T>
class?Rational?{
???public:
??????Rational(const?T&?num?=?0,?const?T&?denom?=?1)
??????{
?????????_numerator?=?num;
?????????_denominator?=?denom;
??????}
#if?OPTION2
??????friend?const?Rational?operator*(const?Rational&?lhs,?const?Rational&?rhs);
#endif
#if?OPTION3
friend?const?Rational?operator?*?(const?Rational&?lhs,?const?Rational&?rhs)
{
???return?Rational(lhs.numerator()?*?rhs.numerator(),?lhs.denominator()?*?rhs.denominator());
}
#endif
??????const?T?numerator()?const?{?return?_numerator;?}
??????const?T?denominator()?const?{?return?_denominator;?}
???private:
??????T?_numerator;
??????T?_denominator;
??????//..
};
#if?OPTION1?||?OPTION2
template?typename?T>
const?RationalT>?operator?*?(const?RationalT>&?lhs,?const?RationalT>&?rhs)
{
???return?RationalT>?(lhs.numerator()?*?rhs.numerator(),?lhs.denominator()?*?rhs.denominator());
}
#endif
int?main(void)
{
???Rationalint>?oneFourth(1,?4);
???Rationalint>?result;
???result?=?oneFourth?*2;
//..
}
當OPTION1為真時,就是item24中的實現(xiàn)方法:用非成員函數(shù)來使得所有的參數(shù)都可以接受類型轉換。
但是編譯不能通過。這說明模板C++的世界是不一樣的: 對于以上代碼的非模板版本,編譯器只需要關心哪個函數(shù)可以調用就可以了;
而引入模板后, 由于一個模板函數(shù)可以有無數(shù)個實例,編譯器首先要知道的是應該生成哪一個實例,然后才是調用。
OPTION1為真時的代碼,這個模板函數(shù)在編譯時期就會碰到問題:模板參數(shù)無法確定。本來說好了接受一個Rational
& 類型參數(shù)的,現(xiàn)在(在main()里 的第三行)給我一個int,叫我怎么辦?
如果我是編譯器的話我會認為這種情況下模板參數(shù)T就是int,于是實例化下面的函數(shù)
const?Rationalint>?operator?*?(?const?Rationalint>&?lhs,?const?Rationalint>&?rhs?);
該函數(shù)可以通過隱式類型轉換接受int類型的參數(shù),順利完成任務!
可是,編譯器很笨,做不到。
于是可以考慮關掉OPTION1,打開OPTION2:把模板非成員函數(shù)當成友元+非成員+模板函數(shù)。為什么需要友元呢?
OPTION1失敗的原因是編譯器無法生成合適的模板函數(shù)實例,它認為沒有合適的實例可以調用,于是編譯失敗。
如果模板函數(shù)變成友元,編譯器首先看到有Rational對象定義出來(main()中第二第三行),就認為會有一個下面的函數(shù) 作為友元:
const?Rationalint>?operator?*?(?const?Rationalint>&?lhs,?const?Rationalint>&?rhs?);
當然,它不會在編譯階段去計較是不是真的有這么一個函數(shù)實例(友元嘛,就是朋友的東東,朋友說有,我就相信有咯~)
于是,編譯通過!
可是編譯通過后,鏈接卻出了問題:到鏈接階段才發(fā)現(xiàn),這個“朋友”的模板函數(shù)根本沒有實例可以調用!(還是一樣的問題, 沒有可以接受int參數(shù)的版本。朋友也不可靠啊……)
問題就在類外部的友元模板函數(shù)僅僅在類中得到了聲明(declaration)而沒有被定義(definition)。對于模板函數(shù),使用者既需要聲明,又需要定義。(比如說 vector v,事實上已經通過制定模板參數(shù)完成了模板函數(shù)的定義。)
于是終于到了最后一步:關掉OPTION1, OPTION2,打開OPTION3。
無可奈何中我們把友元函數(shù)的定義放到了模板類的定義中,相當于把這個“朋友”拉上了船,只要我被定義了,你就一定會被定義。同甘共苦,才算是真的朋友@#¥%
于是編譯通過了,因為編譯器看到了如下的函數(shù)被聲明
const?Rationalint>?operator?*?(?const?Rationalint>&?lhs,?const?Rationalint>&?rhs?);
于是鏈接通過了,因為隨著Rational對象的定義,上面的函數(shù)也實際被定義了出來,編譯器很高興,結果很完美。
事實上這次的讀書筆記記了兩次,當我重新看第一次的筆記時竟然不知所云,于是重新看了一次,也修改了第一版的筆記成為 第二版。
看來,模板真的很能搞……