2011-07-01 73 views
9

可以說我有兩個.cpp文件,file1.cpp和file2.cpp,它們使用std::vector<int>。假設file1.cpp有一個int main(void)。如果我將它們編譯爲file1.o和file2.o,並將兩個目標文件鏈接成一個我可以執行的elf二進制文件。我正在一個32位的Ubuntu Linux機器上編譯。模板化的C++目標文件

我的問題關於編譯器和鏈接器放在一起爲標準::向量的符號:

  • 當鏈接讓我最終的二進制,有沒有代碼重複?鏈接器是否有一組「模板化」的代碼用於f1.o中的代碼,它使用std::vector和另一組std::vector代碼包含f2.o的代碼?

我想這對我自己(我用g++ -g),我看了看我最後的可執行拆卸,而且我發現了矢量構造等方法生成的標籤顯然是隨機的,雖然從f1.o代碼出現調用與f2.o中的代碼相同的構造函數。不過,我無法確定。

如果鏈接器確實阻止代碼重複,它是如何做到的?它必須「知道」什麼模板?它是否始終防止跨多個目標文件多次使用相同模板化代碼的代碼重複?

回答

8

它知道模板是通過name mangling。對象的類型由編譯器以其名稱編碼,並允許鏈接器篩選出同一模板的重複實現。

這是在鏈接過程中完成的,而不是編譯過程中完成的,因爲每個.o文件都可以與任何東西鏈接,因此不能剝離以後可能需要的東西。只有鏈接器才能決定哪些代碼未被使用,哪些模板是重複的,等等。這是通過在對象的符號列表中使用「Weak Symbols」來完成的:鏈接器可以在多次出現時移除的符號(與其他符號相反,比如用戶定義的函數,如果重複並且導致鏈接錯誤,則無法刪除它們)。

+0

這並沒有解決大部分OP的問題。這是真的,但它與這裏提出的問題並不特別相關。 – templatetypedef

+0

@templatetypedef - 我不同意。 *如果鏈接器確實阻止了代碼重複,它是如何實現的?它必須「知道」模板是什麼?*鏈接器知道通過名稱修改。內聯代碼 - 是重複的,鏈接器與它無關。 – littleadv

+0

@ littleadv-你是絕對正確的,但你的答案並不能解決爲什麼鏈接器使用名稱修飾,模板函數的改變名稱與常規函數不同,鏈接器如何決定選擇哪個版本等。我並不是想說這是無關緊要或不正確的,但是除非你已經知道問題的答案,否則我不認爲這個答案會有幫助。 – templatetypedef

0

從技術上講,由於「一個定義規則」,只有一個std::vector<int>,因此代碼應該鏈接在一起。可能發生的情況是,一些內聯代碼會加快執行時間,但可能會產生更多代碼。

如果您有一個文件使用std::vector<int>,另一個文件使用std::vector<unsigned int>,那麼您將有2個類和潛在的大量重複代碼。

當然,vector的編寫者可能會在某些情況下使用一些通用的代碼,例如刪除重複的POD類型。

+0

我不擔心複製std :: vector 和std :: vector 。 「一個定義規則」究竟是什麼?是不是在編譯時爲f1.o和f2.o創建的std :: vector 的定義? – Chris

+0

「一個定義規則」是每個目標文件,在單獨的目標文件之間,如果兩個目標文件都使用它,你將得到兩個不同的向量實例。什麼是內聯 - 將被重複,什麼不是 - 由鏈接器處理。 – littleadv

+0

簡而言之,一個定義規則指出,如果相同的模板實例出現在多個編譯單元中,那麼該模板實例仍只能作爲編譯器的一個定義。基本上,C++在編譯和目標代碼鏈接之間有這個額外的步驟,以便刪除這些額外的模板代碼副本。 – SingleNegationElimination