條款46:需要型別轉換時請為模板定義非成員函式
define non-member functions inside templates when type conversion are desired.
還記得在條款24中,我們提到只有non-member函式才有能力'在所有實參身上實施隱式型別轉換'.今天我們討論的
東西與該條款相關,如果現在的你恰好忘記了前面這條款的內容,那麼我建議你還是先把那條款在消化一下,再開始
這一款吧,呵呵,'磨刀不誤砍柴工'嘛!廢話我就不多說了,我們進入正題.
'只有non-member函式才有能力在所有實參身上實施隱式型別轉換',在泛型程式設計的世界裡,這是否也成立呢?下面我
們來稍微修改一下前面這款例子中的**,然後驗證一下是這句話是否依然成立.
template
class rational;
template
const rationaloperator*(const rational& left_handle_side,
const rational& right_handle_side );
我們希望此時的它能夠一往無前的支援混合式算術運算,我沙沙地寫出下面兩行**進行測試:
rationalone_half( 1, 2 );
rationalresult = one_half * 2; //compile error.
咦?奇怪,難道換成了模板就不行了?事實確實如此!在條款24中編譯器能夠知道我們在嘗試呼叫哪個函式(就是接
受兩個rationals引數的那個operator*啦),但在這裡編譯器卻不知道我們要呼叫哪個函式,它們試圖想出什麼函式被
名為operator*的template具現化出來.但現在的問題是它們沒有足夠大的能耐來完成如此的工作.為了完成具現化工
作,必須先算出t是什麼,於是它們開始了下面的嘗試:
編譯器首先看到了operator*的兩個引數,它們的型別分別是rational(one_half型別)和int(2的型別),每個參
數分開考慮.以one_half進行推導比較容易,operator*的第乙個引數宣告為rational,而傳遞給函式的第一實參類
型是rational,故t一定int.但到了第二個引數推導的時候,問題就來了.operator*宣告的第二個引數為
rational,但實參確實int型別(2).編譯器如何推算t?會不會發生像條款24出現的隱式引數轉換呢?(編譯器適用
rational的non-explicit建構函式將2轉換為rational,進而將t推導為int),但事實卻是很慘酷的:它們沒有
那樣做.因為在template實參推導過程中從不將隱式型別轉換函式納入考慮.這下麻煩了,那有什麼辦法能夠解決這一
問題的呢?別急,其實我們只需要利用乙個事實就可以緩和編譯器在template實參推導方面受到的挑戰,這個事實就是
:template class內的friend宣告式可以指涉某個特定的non-member函式.class templates並不依賴template實參推導
(後者只施行於function templates身上),所以編譯器總是能夠在class rational具現化時得知t.因此,我們的問題
的解決方案就出來了:令rationalclass宣告適當的operator*為其friend函式.
template
class rational;
template
const rationaloperator*(const rational& left_handle_side,
const rational& right_handle_side )
現在對operator*的混合式呼叫就可以編譯了,因為當物件one_half被宣告為rational,class rational
就被具現化出來了,而作為過程的一部分,friend函式operator*也就被自動宣告出來了,此時後者為乙個具現的函式
而不是函式模板羅,因此編譯器可在呼叫它時使用隱式轉換,於是混合式呼叫就基本成功了!基本?難道還有未完成的
工作?呵呵,當你編譯的時候沒問題,是吧?你試試鏈結呢?竟然無法連線!!****!怎麼個情況????
現在我們回頭來思考這個問題.混合式**通過了編譯是因為編譯器知道我們要呼叫哪個函式,但那個函式只被
宣告與rational內,並沒有被定義出來.而我們意圖令此class外部的operator* template提供定義式,但是行不通----
----如果我們自己宣告了乙個函式,就有責任定義那個函式.既然我們沒有提供定義式,聯結器當然找不到它!
最簡單的可行方法就是將operator*函式本體合併至其宣告式內:
template
class rational
};簡單的build一下,我們就可以看到,對operator*的混合呼叫現在可以編譯連線並執行了.哦也!operator*定義體放
到了類的內部成了inline函式,而inline宣告會給類帶來衝擊.為了最小化這種衝擊,我們可以令operator*不做任何事
情,只呼叫乙個定義於class外部的輔助函式,當然,對本條款中的例子而言,這樣做沒有太大的意義,因為operator*只
是乙個單行函式,但對於更複雜的函式而言,這樣做也許就有價值.本款的例子典型長成這樣:
templateclass rational;
template
const rationaldomultiply(const rational& left_handle_side,
const rational& right_handle_side);
template
class rational
...};
好了,今天的討論就到這裡!
請記住:
■ 當我們編寫乙個class template,而它所提供之'於此template相關的'函式支援'所有引數之隱式型別轉換'時,請將
那些函式為'class template內部的friend函式'.
條款46 需要型別轉換的時候請為模板定義非成員函式
看看下面這個例子 1 template2 class rational 8 template9 const rationaloperator const rational lhs,10const rational rhs 11 rational onehalf 1,2 12 rational res...
需要型別轉換時定義為非成員函式
所以為了看我文章的人不會遭遇同樣的問題,2.盡可能循序漸進 還是看不懂可能是我沒有這樣的天賦 標出閱讀的前提知識 3.列出的 自己先跑一遍 c 1.類 2.函式過載 3.型別轉換 整合開發環境 visual studio 2017 class num 不設explicit,提供隱式轉換 constn...
條款46 寧願編譯和連線時出錯,也不要執行時出錯
1,先看乙個例子 class month static const month feb static const month dec int asint const for convenience,make it possible to convert a month to an int priva...