1. 程式人生 > >C++ 模板為什麼不能分離編譯

C++ 模板為什麼不能分離編譯

這兩天寫了一個迴圈佇列的模板類,構建程式碼的時候總是報錯
bin/main.o(.text+0x182):main.cpp: undefined reference to `CCommDataBuf<unsigned char>::CCommDataBuf(unsigned long)'
bin/main.o(.text+0x1a6):main.cpp: undefined reference to `CCommDataBuf<unsigned char>::Appand(unsigned char)'
bin/main.o(.text+0x1b1):main.cpp: undefined reference to `CCommDataBuf<unsigned char>::Dump()'
bin/main.o(.text+0x1e5):main.cpp: undefined reference to `CCommDataBuf<unsigned char>::~CCommDataBuf()'
bin/main.o(.text+0x21b):main.cpp: undefined reference to `CCommDataBuf<unsigned char>::~CCommDataBuf()'

排查原因的時候從標頭檔案包含,程式碼路徑等等都找一遍結果還是不行。

沒辦了突發奇想的把main函式單元的關於迴圈佇列的程式碼全部搬到迴圈佇列的cpp中,結果編譯ok了。

第一反應是不是自動生成的makefile有問題,於是又排查makefile倒騰了一天還是沒解決。

後來在百度上搜原因,找了很久才知道原來模板不支援分離編譯。即模板的宣告和定義都必須在同一個檔案中。

具體原因如下:

C++呼叫外部函式,在編譯時其實只是用一個jmp跳轉指令替代這個呼叫程式碼,而跳轉的地址是一條真正的呼叫指令call。

但是call 的地址是一個任意的無效地址。因為編譯階段本單元是不知道其他單元函式的地址的。於是這個無效地址的修正是放在

連結階段的。連結器根據呼叫函式單元的符號表,修正該無效地址。

這樣問題就來了。因為模板僅在需要的時候才會具現化出來。在我編寫的模板cpp裡頭是沒有具化的只有其定義,因此編譯此單元並不會

生成相應的函式二進位制程式碼。這就導致了在連結階段會在其他呼叫模板單元函式時報未定義的錯誤。

因此把模板cpp程式碼全部放到.h中,問題就解決了。

另外還有一種解決辦法就是在用一個cpp專門用來具現化模板,比如在templateimp.cpp中 template class CCommDataBuf<unsigned char>;

需要說明的是需要對每個不同的呼叫都進行相應的具現。比如有一個int型別的呼叫,就要有一個template class CCommDataBuf<int>;