1. 程式人生 > >friend keyword 對於模板 並不只不過友元!!!

friend keyword 對於模板 並不只不過友元!!!

friend是C++中封裝的漏網之魚。
C++中的friend同意其它的類或者是函式訪問本類的不論什麼成員。甚至是private成員,僅僅要該類宣告其為友元。

但是,在有些情況下,並非同意外界訪問類的內部實現而使用友元。

這就是在 “模板定義” 與 “隱式型別轉換” 之間的一個trick了。

首先,看一個簡單的有理數的模板類。該類定義了有理數,而且實現有理數的乘法。

注:下述程式碼中,將operator*宣告為非成員函式。是由於

“假設你須要對成員函式全部的引數(全部的,當然也就包含this指標啦)進行型別轉換。那麼將該函式宣告為非成員函式”。

也就是說,僅僅有當引數位於引數列的時候,這個引數才是隱式型別裝換的合格參與者。

updated:這裡的引數列指的就是函式的形參列表!!!

可是類的成員函式的那個隱式引數(即this指標)不是隱式轉換的合格參與者!。。

這對C++模板類相同適用。

在上述程式碼中。過載了 * 符號。用於計算兩個有理數之間的乘法。

一個自然而然的道理,假設我們須要支援 有理數 * 自然數 ,這是一個無可厚非的要求。當我們才有以下的呼叫

Rational a = Rational(1,2);

Rational ret = a * 2;

在非模板類中。該函式會將2進行隱式型別轉換為Rational物件。再進行乘積運算。

糟糕!編譯不通過。在非模板類中這是非常正常的事,但是在模板類中卻出現了問題.

簡言之。編譯器陷入了“糾結”的境界!!

。。

以下一一進行分析:

當編譯器看到operator*的呼叫的時候。編譯器不知道我們想要呼叫什麼函式,由於編譯器看到這個模板函式時。第一要做的就是將函式例項化出來,也就是要首先判斷出T的型別,可是幾經周折。發現不行。

首先。為了推匯出T, 編譯器對 * 呼叫的兩個引數進行入手,分別為 Rational 和 int, 由第一個引數能夠非常easy的知道得到 T 為int。 可是在第二個實參呢,編譯器怎麼判斷 T 的型別?? 你或許會說,此時編譯器就應該使用 Rational 的隱式建構函式 啊。

不就能夠 推匯出 T 的型別了嗎。

可是。編譯器絕不會這麼做,由於在 模板實參 的推導過程其中是不會 考慮隱式轉換的。

這是本文最重要的一句話。

因此,在面對這種實參推導 的問題是,friend 便出場了,因為 friend 能夠在 模板類 中指明某個特定的函式。也就是說。在函式呼叫之前。宣告該函式。那麼在函式呼叫時,對應的類模板 就不再須要 依賴於 模板實參的推導了,而僅僅須要對這個友元函式進行引數推導就可以。

因此將operator * 宣告為該類的友元之後。編譯器的行為便不一樣了。

類模板並不依賴於實參的推導(此時operator*函式僅僅是該類的一個模板友元函式)。由於此時的實參推導僅僅施行於該友元模板函式身上,所以編譯器總是可以在Rational類例項化的時候得知T。

最最核心的一段話:

在onehalf 物件被定義的時候,Rational函式就被例項化了,對應的,它的友元函式 operator* 也就被例項化出來了,也就是說,此時的operator * 不再是一個模板函數了,那麼 onehalf * 2 的時候,便是呼叫這個已經被例項化的函數了。於是乎。隱式型別轉換便能夠使用與引數推導了。

可是。另一個問題,便是連結時的問題了,此時,編譯器知道我們要呼叫的是哪個函數了,可是如今那個函式僅僅是被宣告在Rational類中,並沒有實現它。 假設我們在Rational外部定義該函式,這是行不通的。

因此,僅僅在類定義體中宣告該函式,假設不定義的話,聯結器便會發出抱怨,找不到定義體。

(updated:這裡該函式的定義必須由類定義負責,否則該函式就必須是如今類的外面,那麼自然而然該函式就必須得是模板函式,那麼引數推導又不起作用了。!!

因此必須定義在函式的內部。!!)

因此,將 operator* 宣告為友元函式而且將實現定義在類中。

於是,正確的Rational模板類的定義為:

執行結果為:

大功告成!!

上述程式碼成功的實現了我們的功能!。!

於是,在類模板定義時。出現了相同的話,假設 在編寫一個類模板的時候,而 該類的與模板相關的函式 須要支援函式引數

隱式轉換的時候,將該函式定義為模板類類中的friend函式。

updated:模板函式對於引數的型別推導是絕對不會考慮 “建構函式的隱式型別轉換的”!

這與一般的函式呼叫是不一樣的。因此我們假設須要對函式的引數進行型別推導,那麼就須要將該函式定義為非模板型別,這是編譯器就會陷入兩難的境界:

然而。對於一般的函式(非模板函式)。編譯器是會進行引數推導的(包含呼叫non-explicit建構函式)!

。!

1在引數推導時使用隱式轉換 2 為了讓這個函式被具現化,我們又須要將它宣告在模板類的內部!

friend!

!!!將函式的宣告與定義均置於類內部。