[问题] 如何只特化参数类别的其中一个函式

楼主: 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