[問題] 如何只特化參數類別的其中一個函式

作者: alan23273850   2023-02-11 00:26:53
開發平台(Platform): (Ex: Win10, Linux, ...)
Ubuntu 20.04
編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出)
g++
額外使用到的函數庫(Library Used): (Ex: OpenGL, ...)
問題(Question):
給定一個 template class 全部的 member function definition,有沒有辦法對於某個 sp
ecialized class 來說我只特化它的其中一個 function,而且要用 original definition?
由於我必須複製它原始整份的實作到特化的函式定義裡面,才會編譯成功。想請問各位板大
有沒有不用複製整份程式碼也能只特化其中一個函式的方法?穴穴大家。
餵入的資料(Input):
預期的正確結果(Expected Output):
錯誤結果(Wrong Output):
程式碼(Code):(請善用置底文網頁, 記得排版,禁止使用圖檔)
我現在程式碼有定義一個可以根據 Leaf type 去特化的一個二元樹模板
template <typename Leaf> struct AUTOQ::Util::BinaryTree
.h 檔有放 prototype, .cpp 檔有放 implementation
那我們都知道如果要針對某種 Leaf 例如 int 去特化這棵二元樹,那必須在所有 *.cpp
的末尾加上 template struct AUTOQ::Util::BinaryTree<int>; 這句話才能把所有函式
實作特化出來。
但現在問題是,我的模板有包含一個兩棵二元樹相加的函式,而這個在 Leaf 是 string
的時候是無法支援的,因此我只想實作例如 AUTOQ::Util::BinaryTree<string> print
函式,那現在的狀況就是:
(1) 單純在有實作 print 的 .cpp 底下補上
template <> void AUTOQ::Util::BinaryTree<string>::print();
這句話,此時 main 函式會通報找不到這個實作,而無法編譯成功。
(2) 我在這個有實作 print 的 .cpp 底下補上
template <> void AUTOQ::Util::BinaryTree<string>::print() {
// 複製原本模板裡面的程式碼
}
這個新的實作,main 函式就找得到了。
所以問題就是,如何採用類似 (1) 的手法,使得我不需要再複製一次程式碼,就能直接
使用模板的實作呢?
補充說明(Supplement):
作者: alan23273850   2023-02-11 00:27:00
程式碼我明天再補
作者: LPH66 (-6.2598534e+18f)   2023-02-11 02:58:00
繼承該特定 specialization 並 override 掉你要特化的函數這樣可以嗎?噢等等, 如果該函數沒有 virtual 那 override 抓不到原程式碼呼叫被蓋掉的函數的狀況不過如果你動得到模版原始碼的話, 加個 virtual 應該就行了
作者: jack7775kimo (阿龐)   2023-02-11 07:55:00
CRTP?
作者: alan23273850   2023-02-11 11:51:00
我補上程式碼了,這個問題對我來說很重要,如果獲得解答的話我將奉送大量批幣!
作者: nh60211as   2023-02-11 12:04:00
把實作放在header
作者: alan23273850   2023-02-11 12:07:00
@jack7775kimo 大那個關鍵字我剛剛查了一下好像很酷但不確定能不能用在這裡@nh60211as 大的解法我可能需要更具體的理由
作者: Fenikso (薪水小偷)   2023-02-11 13:57:00
能動header的話就用concept或enable_if處理吧https://godbolt.org/z/36E585xr8
作者: LPH66 (-6.2598534e+18f)   2023-02-11 14:18:00
template 其實會常見把實作寫在 header 裡的做法理由是模版實現只在給定所有模版參數之後
作者: alan23273850   2023-02-11 14:19:00
我理解 @Fenikso 大大正面表列的作法,但是這樣會有
作者: LPH66 (-6.2598534e+18f)   2023-02-11 14:19:00
除非像你這樣特別去引用一個模版把它特化出來
作者: LPH66 (-6.2598534e+18f)   2023-02-11 14:20:00
不然你是無法對別的 TU 裡引用的模版去產生程式碼的
作者: alan23273850   2023-02-11 14:20:00
是,我不知道為何內文 (1) 的作法編譯器會不通過。
作者: LPH66 (-6.2598534e+18f)   2023-02-11 14:21:00
把實作寫在標頭就把很多決定模版的地方延後到使用處生成
作者: alan23273850   2023-02-11 14:25:00
哦哦哦 但是我好像漸漸對 @Fenikso 大大的答案有點感覺了,只要實作的地方先加註 prerequisites 確保實作完畢,才開始實現我這個函式,好像就能避免掉我那個問題,我禮拜一有空會試試看,如果 OK 的話就奉送 @Fenikso 一個大禮!也謝謝 @LPH66 大大的解說,兩個我都會試試看。
作者: wulouise (在線上!=在電腦前)   2023-02-12 00:17:00
為甚麼你要寫在source file內感覺才是癥結點
作者: alan23273850   2023-02-12 00:36:00
一般不是都鼓勵分成兩個檔案嗎?不然理由是什麼呢
作者: closer76 (克樓瑟)   2023-02-12 09:03:00
其實你去看大部分使用template 的函式庫,都是直接把實作寫在 header files 裡的。原因是大部分的編譯器都不支援有 template 的類別、函式內容另外定義。其實你可以用編譯器的角度想想:template 其實就是編譯器要幫你特化,所以編譯器需要知道 template 的原始碼。而 C++ 又沒有強制要求實作和宣告的檔名一定要有關連,那麼編譯器就得搜尋整個專案,才能找到定義的原始碼。增加編譯器實作成本。那為何不乾脆要求放在一起呢?
作者: alan23273850   2023-02-12 09:49:00
@closer76 如果不是 template 的話不也是要搜尋整個專案嗎?
作者: LPH66 (-6.2598534e+18f)   2023-02-11 02:59:00
這樣可以嗎?噢等等, 如果該函數沒有 virtual 那 override 抓不到原程式碼呼叫被蓋掉的函數的狀況不過如果你動得到模版原始碼的話, 加個 virtual 應該就行了
作者: closer76 (克樓瑟)   2023-02-12 13:11:00
沒有 template 的話,至少可以先編成 obj,symbol table對 linker 來說也是比較容易處理的資料C++ 原本是用 export 關鍵字來做這件事的,但因為太難做,所以後來被移出標準了。可以參考這裡:https://stackoverflow.com/questions/5416872/裡面有提到 C++20 有 module 功能,但我沒研究過
作者: Fenikso (薪水小偷)   2023-02-12 23:52:00
不知道你怎麼改的 報error的寫法丟上來看看?
作者: alan23273850   2023-02-13 19:52:00
感謝樓上大大持續追蹤,請先切換到 https://github.com/alan23273850/AutoQ/commit/8ebd44aeeaa42a2e68ee80779147b84b17301e42 這個記錄點,按照 readme 去編譯會發現可以過,接著把此變化內的 aut_operation.cc 最底部 4 個 Automata<Predicate> 實作的函式直接改成分號結尾,然後就會發現不能編譯了 :(
作者: Fenikso (薪水小偷)   2023-02-14 14:21:00
啊因為你不能instantiate那些不該出現的functionhttps://bit.ly/3Xt3iN1 把所有不該給Predicate用的東西加上requires這樣就行了
作者: alan23273850   2023-02-14 19:01:00
感謝 @Fenikso 大大幫我改程式碼,我有空再消化消化看懂再加碼 1000P所以那個 requires 裡面的三行是故意去呼叫看看那三個函式有沒有支援嗎?可是看起來好像 runtime 會實際去執行的程式碼。
作者: Fenikso (薪水小偷)   2023-02-14 21:09:00
對看起來很像 runtime 但不是 XD

Links booklink

Contact Us: admin [ a t ] ucptt.com