【轉】類模版的分離編譯問題

轉自http://blog.csdn.net/woshishuizzz/article/details/8648440前端

 模板不是數據類型,只能算是一種行爲集合的表示。編譯器在使用模板時,經過更換模板參數來建立數據類型。這個過程就是模板實例化(Instantiation), 從模板類建立獲得的類型稱之爲特例(specialization),說白了就是建立了一個新類型。 模板實例化取決於編譯器可以找到可用代碼來建立特例(稱之爲實例化要素,point of instantiation),也就是說,編譯器不但要看到模板的聲明,還要看到模板的定義。面試

1. 爲何類模版不能分離編譯,這要從模板的實例化過程提及:函數

    1)以分離形式寫出的模版類(以tem.h和tem.cpp爲例,另外還有主函數main.cpp),在編譯main.cpp時因爲只能看到模板聲明而看不到實現,所以不會建立新的類型,但此時不會報錯,由於編譯器認爲模板定義在其它文件中,就把問題留給連接程序處理。ui

    2)編譯器在編譯tem.cpp時能夠解析模板定義並檢查語法,但不能生成成員函數的代碼。由於要生成代碼,須要知道模板參數,即須要一個類型,而不是模板自己。.net

    3)這樣,連接程序在main.cpp 或 tem.cpp中都找不到新類型的定義,因而報出無定義成員的錯誤。blog

另外,實例化是惰性的,只有用到該函數時纔會去對模版中的定義進行實例化。ci

2. 如何解決這個問題?有三種方式:get

    1)在實例化要素中讓編譯器看到模板定義。(即寫到一個文件裏)編譯器

    2)用一個新的文件來顯式地實例化類型,這樣連接器就能看到該類型。it

    3)使用export關鍵字。

    前二種方法一般稱爲包含模式,第三種方法則稱爲分離模式。第一種方法意味着在使用模板的轉換文件中不但要包含模板聲明文件,還要包含模板定義文件,這樣編譯器就能看到模板的聲明和定義。這樣作的缺點是編譯文件會變得很大,顯然要下降編譯和連接速度。第二種方法,經過顯式的模板實例化獲得類型,也就是將全部的顯式實例化過程安放在另外的文件中(好比tem_extend.cpp)。這樣新類型不是在main.cpp中產生,而是在tem_extend.cpp中產生,連接器就可以找到它的定義。用這種方法,不會產生巨大的頭文件,加快編譯速度,並且頭文件自己也顯得更加「乾淨」和更具備可讀性。但這個方法不能獲得惰性實例化的好處,即它將顯式地生成全部的成員函數。第三種方法是在模板定義中使用export關鍵字,剩下的事就讓編譯器去自行處理了。

3. 其餘

    vc和gcc都只支持h文件裏反過來include cpp文件的那種所謂的「模板分離編譯」,雖然把h和cpp分開了,但編譯的時候仍是合成了一個h文件,真正的分離編譯是export關鍵字,而export關鍵字,任何主流編譯器不支持而且之後都不打算支持。「靠譜的編譯器都不支持模板分離編譯,支持模板分離編譯的編譯器都不靠譜」。

    另外,EDG是第一個支持C++ export關鍵字的前端編譯器,可是它不推薦使用這個關鍵字,之後將再也不支持export。

    參考:1)模板分離編譯過程,and 2)昨天面試的幾道小題

4. 另外的另外

    不少C/C++編譯器對函數參數的壓棧都是自右向左的,即最右的參數被首先push到棧底,而後load到寄存器中進行下一步運算。這在一些多個參數均帶有同一變量的自增或自減的函數中會出現難以想象的結果。

相關文章
相關標籤/搜索