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