1. 程式人生 > >C++模板定義和實現分離導致的編譯錯誤

C++模板定義和實現分離導致的編譯錯誤

今天在實現一個二叉樹的時候,用到了模板,沒在意,和平時一樣寫了兩個檔案:BinaryTree.h,BinaryTree.cpp。思路和平時一樣,h檔案中定義了模板類,然後在cpp檔案中實現了一些功能函式。

然後在test.cpp中測試,遇到了一些問題、

問題出現如下:

  • 使用g++直接編譯的時候提示,基本上都是undefined reference to錯誤:
test_binarytree.cpp:(.text+0x16e): undefined reference to `BinaryTree<std::string>::BinaryTree()'
......
  • 分別編譯單獨檔案,均可生成 .o檔案,所以排除單個檔案本身的錯誤
  • 連線兩個o檔案時,仍然提示上述錯誤,很明顯,連線時,尋找函數出錯了。

網上查的資料如下:

編譯器不支援 模板類定義和實現的分離,即類定義和實現不能分開放在h標頭檔案和cpp原始碼檔案中;

經過測試,貌似把cpp檔案的東西都搬到.h檔案中,如果cpp檔案還在,都還會提示該問題,所以,除了.h中放實現程式碼之外,還得把空的cpp檔案刪除。

從專案中刪除後編譯成功,但後來重新增加這個.cpp檔案,貌似還是能編譯成功。

上述的說法親測正確,但仍有不足,補充資料留作記錄:

  1. 首先C++中有分離編譯的概念:

    • 分離編譯模式(Separate Compilation Model)

      允許在一處翻譯單元中定義(define)函式、型別、類物件等,在另一處翻譯單元引用它們。編譯器(Compiler)處理完所有翻譯單元后,連結器(Linker)接下來處理所有指向 extern 符號的引用,從而生成單一可執行檔案。該模式使得 C++ 程式碼編寫得稱心而優雅。

    • 然而該模式卻馴不服模板(Template)。標準要求編譯器在例項化模板時必須在上下文中可以檢視到其定義實體;而反過來,在看到例項化模板之前,編譯器對模板的定義體是不處理的——原因很簡單,編譯器怎麼會預先知道 typename 實參是什麼呢?因此模板的例項化與定義體必須放到同一翻譯單元中。

  2. export關鍵字

標準 C++ 為此制定了“模板分離編譯模式(Separation Model)”及 export 關鍵字。然而由於 template 語義本身的特殊性使得 export 在表現的時候效能很次。

用法此處就不說了,因為C++標準在11中將其丟棄了,所以不建議使用。

另外網上還有一些包含cpp檔案的做法之類的,也不做介紹,我覺得不夠規範化。因為模板的制定本身就是應該為例項化而開源。所以我比較贊同將模板類的定義和實現放在同一個檔案的做法。