看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《moebear (萌熊)》之銘言: : 開發平台(Platform): (Ex: Win10, Linux, ...) : win10/linux : 編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出) : GCC/VC++ : 額外使用到的函數庫(Library Used): (Ex: OpenGL, ...) : 問題(Question): : 請問程式碼中第6/22/28行,這三個ostream之間的關聯性是什麼? : 25行以上是助教給的程式碼,但是我寄信問助教,他只說這是約定俗成的寫法 囧。 : 1.為什麼第6行是必備的? 我的理解中,提前宣告是因為實作在後面,中間可能有人用到 : 但是中間到底是誰用到呢? 22行嗎? 那為什麼22行會需要用到第6行的宣告呢? : 2.第22行的<>是什麼意思呢? 我覺得看起來很像是某種template, : 但是中間又不能塞T進去。 : 3.第28行是我自己寫的,我試過很多方法, : 但是好像只有這樣寫才可以,跟他關聯的好像是第6行,而不是第22行。 : 總之就是這三行之間的關係,以及為什麼22行要這樣寫? 先講結論: 根本原因是 CirDequeTemplate 是一個 template class 的關係 如果你只是在寫一個非 template class 的 ostream << operator 的話 事情其實很簡單: class IntCirDeque { //... friend ostream& operator << (ostream&, const IntCirDeque&); }; ostream& operator << (ostream& out, const IntCirDeque& icd) { //... } 這樣就行了, 甚至那行 friend 還能充當前置宣告使用 也就是對只引用 IntCirDeque 定義而沒有 operator << 實作的地方也能呼叫它 (有個限制就是: 它要能被 ADL 找到才行, 這裡顯然 ADL 能找得到 不過這跟原問題無關就表過不提) 關於這種寫法的細節你應該清楚, 例如為什麼它要是非成員再寫 friend, 這裡略過 ==== 那麼為什麼當掛上 template 事情就有點微妙的不一樣了? 首先, 先看你的實作 (#27~#28): template<class T> std::ostream & operator<< (std::ostream &s, const CirDequeTemplate<T> &cird) 你應該在嘗試時注意到了, 因為 cird 是一個 template class 你必須要對每個 T 都要能產生一份這個 operator 才行 如你所觀察到的, 跟它相關的是 #6 事實上你這兩行扣掉變數名其實是必須要跟 #6 一模一樣才行 這正是在達成上面提的「對每個 T 都要能產生一份這個 operator」 這個寫法其實就只是一個普通的 template 函數的寫法而已 跟 template class 一樣, 在前面掛上 template <class T> 然後下面用 T 代換 到這裡應該回答了你的問題 3 (一個題外話, 你這裡的實作不能用 printf, 因為當 T 不是 int 時就爆了 你應該要 << 進那個 ostream& 裡面, 跟 cout 一樣的用法只不過 cout 換成這個參數 是說你都在學 C++ 了為什麼還寫 printf...) ==== 剛才提到「對每個 T 都要能產生一份這個 operator」 所以 class 裡面宣告 friend 時必須要宣告 「有跟我一樣的 T 的 operator << 是我的朋友」 這裡需要提一個你應該也知道的規則: 單獨 template 名並不是一個完整的名字, 它一定要有辦法知道它的 <> 裡是什麼型別 在大多數時候 <> 都不能省略, 否則編譯器不會知道這個名字是個 template 名 就算你的 <> 裡要用其他資訊推導也是一樣 <> 能省略的地方只有你在自己裡面指名自己時可以省略 (我知道這裡有人要提 template 函數呼叫, 那個最後提) (這裡還必須要真的是指名自己, 也就是 template 參數都要一模一樣才行 如果不一樣那就還是得乖乖寫出來) 這就是這裡的 <> 的用途: 表示這個 operator << 是一個 template 名字 那 <> 裡面的東西也不必每次都一一指定, 能夠推導的就能省略 這個狀況裡這裡面是可以塞 T 的: friend ostream& operator << <T> (ostream&, const CirDequeTemplate<T>&); 即是重覆一次 #6 / #28 的宣告 只不過把宣告用的前置 template <class T> 換成指名用的後置 <T> 但是這個 T 其實是可以從參數推導出來的: 這個宣告的第二參數是一個自己這種物件, 因此這時的 T 是確定的: 跟我自己一樣的 T 因此我們可以省略前面的 operator << <T> 裡的 T, 就成了 operator << <> 了 後面的 <T> 是屬於上面所說指名自己時可省略的狀況, 所以也就不寫了 這樣就成了 #22 的寫法: friend ostream& operator<< <>(ostream&, const CirDequeTemplate&); 以上回答問題 2 ==== 剛才也提到, #22 的寫法裡的 operator << 是個 template 名 既然是 template 名那在前面就必須要有對應的 template 宣告才能使用 #6 就正是這個 operator << 所需要的前置 template 宣告 有了 #6 的宣告, #22 的 operator << 才能知道是一個 template 名字 才能跟後續的 template 定義連起來 這裡你不能像非 template 版本一樣把 template 宣告跟 friend 合起來 原因是你所 friend 的只有這特別的一個 operator << 而已, 不是整個 template 以上回答問題 1 同樣的理由 #5 為 #6 裡的 CirDequeTemplate 這名字進行前置宣告 ==== 因此全部總結起來的話就是這樣: #28 為了 template class 所以寫成一個單純的 template 函數 #22 嘗試宣告某個特定的 #28 為 friend, 為此需要 #6 前置宣告 #28 的存在 ==== 最後回頭解釋一下為什麼上面沒提 template 函式呼叫做為省略 <> 的狀況 這是因為函式呼叫是個不一樣的狀況 編譯器會取出這個名字, 看看有沒有一般函數及 template 函數有這名 如果有看到 template 函數, 會嘗試推導要用什麼 template 參數才能符合呼叫方型態 所有能推導出來的版本跟(如果找得到的)一般函數再一起進行 overload resolution (是的, 我們可以定義同一個名字有非 template 函數和 template 函數) 也就是說, 函數呼叫這個指名並不是直接指名某個 template 函式定義 因此不是屬於「提到 template 名」的狀況 (然後順帶一提, C++17 把這個推導推廣到 constructor 呼叫也適用了 在 C++17 的文件當中叫做 deduction guide, 這裡表過不提) -- Ace Snake Santa Clover Junpei June Seven Lotus 9th man cabin kitchen casino shower operating room laboratory T H E chart captain quarter confinement torture room steam engine room cargo chapel library study incinerator Gigantic Q director office security N O N A R Y archives control laboratory pec treatment garden pantry gaulem bay rec room crew quarters infirmary lounge elevator Tenmyouji Quark Dio G A M E S Luna Phi Sigma Alice Clover K -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 180.177.29.238 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1496956504.A.217.html ※ 編輯: LPH66 (180.177.29.238), 06/09/2017 05:15:23 ※ 編輯: LPH66 (180.177.29.238), 06/09/2017 05:19:08
hunandy14: 嗚哇 c++17那個好方便 長知識了 06/09 10:07
TianBonBon: 超猛 06/09 11:18
moebear: 謝謝! 我懂了 題外話那邊是我習慣不好,之後我多加留意 06/09 13:36
phishingphi: Effective C++ Item 46 也有類似這裡的用法,不過是 06/10 21:50
phishingphi: 對 implicit type conversion 的 template 用法。 06/10 21:50
phishingphi: 可供有興趣的人參考。 06/10 21:51
xvid: 推 06/15 20:41