C++模板編譯模型

來源:互聯網
上載者:User

標籤:isp   amp   post   為我   reference   clu   exit   其他   一個   

一:傳統的編譯模型

         使用C/C++進行編程時,一般會使用標頭檔以使定義和聲明分離,並使得程式以模組方式組織。將函式宣告、類的定義放在標頭檔中,而將函數實現以及類成員函數的定義放在獨立的檔案中。

         但是對於模板來說,這種方式是行不通的,具體的例子如下:

         首先是包含模板聲明的標頭檔temp.h:

//temp.h#ifndef TEMP_H#define TEMP_Htemplate<typename T>int compare(const T &a, const T &b);template<typename T>class testtemp{public:    testtemp(const T &a):m_value(a){}    void display();private:    T   m_value;};#endif

    該標頭檔中包含了一個函數模板的聲明,以及一個類模板的定義。

         下面是包含模板定義的源碼檔案temp.cpp:

//temp.cpp#include <iostream>#include "temp.h"template<typename T>int compare(const T &a, const T &b){    if (a < b)  return -1;    if (b < a)  return 1;    return 0;}template <typename T> void testtemp<T>::display(){    std::cout << m_value << std::endl;}

         下面是主函數檔案main.cpp:

//main.cpp#include <iostream>#include "temp.h"int main(){    int a = 1, b = 3;    int res;    testtemp<int> tt(4);    tt.display();    res = compare(a, b);    std::cout << "res is " << res << std::endl;}

  

         對上面的檔案編譯產生可執行檔時,會報錯:

# g++ -o main main.cpp temp.cpp/tmp/ccNwfO8x.o: In function `main‘:main.cpp:(.text+0x47): undefined reference to `testtemp<int>::display()‘main.cpp:(.text+0x5a): undefined reference to `int compare<int>(int const&, int const&)‘collect2: error: ld returned 1 exit status

   

         報錯的原因如下:

         C++中每一個對象所佔用的空間大小,都是在編譯的時候就確定的。在編譯階段,源碼檔案main.cpp將包含模板聲明的標頭檔temp.h包含進來之後,編譯器就需要為main.cpp中涉及到的每個對象產生合適的記憶體布局,為每個函數產生相應的指令。

         當源碼檔案main.cpp中涉及到模板類成員函數或者模板函數的調用時,因為模板函數的定義在另一個源碼檔案temp.cpp中,編譯器目前僅僅知道它們的聲明。所以,在main.cpp中調用到的的testtemp<int>::display函數,以及int compare<int>(int const&, int const&)函數,編譯器認為這些函數的實現是在其他源碼檔案中的,編譯器不會報錯,因為連接器會最終將所有的二進位檔案進行串連,從而完成符號尋找,形成一個可執行檔。

         儘管編譯器也編譯了包含模板定義的源碼檔案temp.cpp,但是該檔案僅僅是模板的定義,而並沒有真正的執行個體化出具體的函數來。因此在連結階段,編譯器進行符號尋找時,發現源碼檔案中的符號,在所有二進位檔案中都找不到相關的定義,因此就報錯了。

 

二:模板的編譯模型

         當編譯器看到模板定義的時候,它不立即產生代碼。只有在看到用到模板時,如調用了函數模板或調用了類模板的對象的時候,編譯器才產生特定類型的模板執行個體。

         一般而言,當調用函數的時候,編譯器只需要看到函數的聲明。類似地,定義類類型的對象時,類定義必須可用,但成員函數的定義不是必須存在的。因此,應該將類定義和函式宣告放在標頭檔中,而普通函數和類成員函數的定義放在源檔案中。

         模板則不同:要進行執行個體化,編譯器必須能夠訪問定義模板的原始碼。當調用函數模板或類模板的成員函數的時候,編譯器需要函數定義,需要那些通常放在源檔案中的代碼。

 

         標準 C++ 為編譯模板代碼定義了兩種模型。分別是包含編譯模型和分別編譯模型。

         所謂包含編譯模型,說白了,就是將函數模板的定義放在標頭檔中。因此,對於上面的例子,就是將temp.cpp的內容都放到temp.h中。

         包含編譯模型有個問題,如果兩個或多個單獨編譯的源檔案使用同一模板,這些編譯器將為每個檔案中的模板產生一個執行個體。因此給定模板會產生多個相同的執行個體,在連結的時候,編譯器會選擇一個執行個體化而丟棄其他的。

 

         在分別編譯模型中,編譯器會為我們跟蹤相關的模板定義。但是,我們必須讓編譯器知道要記住給定的模板定義,因此需要使用 export 關鍵字。但是,實際上很多編譯器都不支援這個關鍵字,而且C++11 將這個關鍵字設定為 unsued 和 reserved 了。

 

         所以,結論就是,把模板的定義和實現都放到標頭檔中。

 

參考:

http://gaunthan.leanote.com/post/C-%E6%A8%A1%E6%9D%BF%E7%9A%84%E7%BC%96%E8%AF%91%E6%A8%A1%E5%9E%8B

https://www.zhihu.com/question/20630104

C++模板編譯模型

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.