詳解C++函數(shù)模板與分離編譯模式
1.分離編譯模式
一個(gè)程序(項(xiàng)目)由若干個(gè)源文件共同實(shí)現(xiàn),而每個(gè)源文件單獨(dú)編譯生成目標(biāo)文件,最后將所有目標(biāo)文件連接起來(lái)形成單一的可執(zhí)行文件的過(guò)程稱(chēng)為分離編譯模式。
2.使用函數(shù)模板在鏈接時(shí)出錯(cuò)
在C++程序設(shè)計(jì)中,在一個(gè)源文件中定義某個(gè)函數(shù),然后在另一個(gè)源文件中使用該函數(shù),這是一種非常普遍的做法。但是,如果定義和調(diào)用一個(gè)函數(shù)模板時(shí)也采用這種方式,會(huì)發(fā)生編譯錯(cuò)誤。
下面的程序由三個(gè)文件組成:func.h用來(lái)對(duì)函數(shù)模板進(jìn)行申明,func.cpp用來(lái)定義函數(shù)模板,main.cpp包含func.h頭文件并調(diào)用相應(yīng)的函數(shù)模板。
/***func.h***/ template<class T> void func(const T&); /***end func.h***/ /***func.cpp***/ #include <iostream> using namespace std; #include "func.h" template<class T> void func(const T& t) { cout<<t<<endl; } /***end func.cpp***/ /***main.cpp***/ #include <stdio.h> #include "func.h" int main() { func(3); } /***end main.cpp***/
這是一個(gè)結(jié)構(gòu)非常清晰的程序,但是它不能通過(guò)編譯。在VS2017下的出錯(cuò)信息是:
error LNK2019: 無(wú)法解析的外部符號(hào) "void __cdecl func< int>(int const &)" (??$func@H@@YAXABH@Z)
原因出現(xiàn)在分離編譯模式上。在分離編譯模式下,func.cpp會(huì)生成一個(gè)目標(biāo)文件為func.obj,由于在func.cpp文件中,并沒(méi)有發(fā)生函數(shù)模板調(diào)用,所以不會(huì)將函數(shù)模板func<T>
實(shí)例化為模板函數(shù)func<int>
,也就是說(shuō),在func.obj中無(wú)法找到關(guān)于模板函數(shù)func<int>
的實(shí)現(xiàn)代碼。在源文件main.cpp中,雖然函數(shù)模板被調(diào)用,但由于沒(méi)有模板代碼,也不能將其實(shí)例化。也就是說(shuō),在main.obj中也找不到模板函數(shù)func<int>
的實(shí)現(xiàn)代碼。這樣,在鏈接的時(shí)候就會(huì)出現(xiàn)func<int>
沒(méi)有定義的錯(cuò)誤。
3.解決辦法
3.1將函數(shù)模板的定義放到頭文件
一個(gè)簡(jiǎn)單的解決辦法就是將函數(shù)模板func<T>
的定義寫(xiě)到頭文件func.h中。這樣的話,只要包含了這個(gè)頭文件,就會(huì)把函數(shù)模板的代碼包含進(jìn)來(lái),一旦發(fā)生函數(shù)調(diào)用,就可以依據(jù)函數(shù)模板代碼將其實(shí)例化。這個(gè)辦法雖然簡(jiǎn)單可行,但是有如下不足。
(1)函數(shù)模板的定義寫(xiě)進(jìn)了頭文件,暴露了函數(shù)模板的實(shí)現(xiàn)細(xì)節(jié)。
(2)不符合分離編譯模式的規(guī)則,因?yàn)榉蛛x編譯模式要求函數(shù)原型申明放在頭文件,定義放在源文件。
注意: 這樣做,如果在多個(gè)目標(biāo)文件中存在相同的函數(shù)模板實(shí)例化后的模板函數(shù)實(shí)體,鏈接時(shí)并不會(huì)報(bào)函數(shù)重定義的錯(cuò)誤,這與普通函數(shù)不同,因?yàn)榫幾g器會(huì)對(duì)實(shí)例化后的重復(fù)的模板函數(shù)實(shí)體進(jìn)行優(yōu)化,只保留一份代碼實(shí)體。如果不同的源文件中都保留一份函數(shù)模板實(shí)體,會(huì)造成代碼冗余,實(shí)際上,這也是一種代碼冗余的解決辦法。
3.2仍然采用分離編譯模式
有什么辦法可以讓函數(shù)模板實(shí)例化時(shí)能夠找到相應(yīng)的模板函數(shù)的代碼呢?一個(gè)可能的解決辦法就是使用關(guān)鍵字export。也就是說(shuō),在func.cpp里定義函數(shù)模板的時(shí)候,將函數(shù)模板頭寫(xiě)成:
export template<class T> void func(const T& t);
這樣做的目的是告訴編譯器,這個(gè)函數(shù)模板可能再其他源文件中被實(shí)例化。這是一個(gè)對(duì)程序員來(lái)說(shuō)負(fù)擔(dān)最輕的解決辦法,但是,目前幾乎所有的編譯器都不支持關(guān)鍵字export,包括VC++和GNU C++。
3.3顯示實(shí)例化
顯示實(shí)例化也稱(chēng)為外部實(shí)例化。在不發(fā)生函數(shù)調(diào)用的時(shí)候?qū)⒑瘮?shù)模板實(shí)例化,或者在不使用類(lèi)模板的時(shí)候?qū)㈩?lèi)模板實(shí)例化稱(chēng)之為模板顯示實(shí)例化。
上面遇到的問(wèn)題是main.obj和func.obj中找不到模板函數(shù)func<int>
的實(shí)現(xiàn)代碼,那么就在func.cpp中將函數(shù)模板func<T>
顯示實(shí)例化為模板函數(shù)func<int>
。
template void func<int>(const int&); //函數(shù)模板顯示實(shí)例化
這樣,就可以在func.cpp產(chǎn)生模板函數(shù)func<int>
的實(shí)例化代碼,編譯之后就會(huì)產(chǎn)生函數(shù)的二進(jìn)制代碼,供其它源文件鏈接,程序就可以正常運(yùn)行。當(dāng)類(lèi)模板的成員函數(shù)的實(shí)現(xiàn)定義在源文件中,通過(guò)模板類(lèi)的對(duì)象調(diào)用成員函數(shù)時(shí)也會(huì)出現(xiàn)找不到函數(shù)定義的錯(cuò)誤,可以使用同樣的方法解決,不再贅述。
以上就是詳解C++函數(shù)模板與分離編譯模式的詳細(xì)內(nèi)容,更多關(guān)于C++函數(shù)模板與分離編譯模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++逆向分析移除鏈表元素實(shí)現(xiàn)方法詳解
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(203.移除鏈表元素),本篇文章通過(guò)逆向分析的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2022-11-11C語(yǔ)言?xún)?nèi)存的動(dòng)態(tài)分配比較malloc和realloc的區(qū)別
這篇文章主要介紹了C語(yǔ)言?xún)?nèi)存的動(dòng)態(tài)分配比較malloc和realloc的區(qū)別,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是本文的詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07詳解如何在VS2019和VScode中配置C++調(diào)用python接口
這篇文章主要介紹了詳解如何在VS2019和VScode中配置C++調(diào)用python接口,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12深入剖析C語(yǔ)言中qsort函數(shù)的實(shí)現(xiàn)原理
這篇文章主要介紹了C語(yǔ)言中qsort函數(shù)的實(shí)現(xiàn)原理,本文將從回調(diào)函數(shù),qsort函數(shù)的應(yīng)用,qsort函數(shù)的實(shí)現(xiàn)原理三個(gè)方面進(jìn)行講解,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-03-03