2012-01-28 72 views
3

比方說,我有以下代碼:模板類在C++中以不同編譯單元編譯多次?

// templateClass.h 
#ifndef TEMPLATE_CLASS_H 
#define TEMPLATE_CLASS_H 

template <typename T> 
class tClass 
{ 
public: 
    tClass(); 
}; 

#endif 

// templateClassDef.inl 
#ifndef TEMPLATE_CLASS_DEF_INL 
#define TEMPLATE_CLASS_DEF_INL 

template <typename T> 
tClass<T>::tClass() 
{ 
} 

#endif 

// normalClass.h 
#include "templateClass.h" 

class normal 
{ 
public: 
    normal(); 
}; 

// normalClass.cpp 
#include "normalClass.h" 
#include "templateClassDef.inl" 

normal::normal() 
{ 
    tClass<int> a; 
} 

// main.cpp 
#include "templateClass.h" 
#include "templateClassDef.inl" 

#include "normalClass.h" 

int main() 
{ 
    tClass<int> a; 
    normal b; 

    return 0; 
} 

注意,不被包含在報頭中的inl文件作爲它通常,而是包含在源文件(我知道這是不是標準的方式......這只是一個例子)。請注意,normalcClass.cpp正在實例化tClass<int>,因此main.cpp也是如此。

我很好奇,編譯器是否必須從模板類在每次遇到一個明確的實例化時構建的實例,認爲這是同一類型(即tClass<int>即使發生在不同的翻譯單位都實例(normalClass.cppmain.cpp)?此外,這將增加在編譯時(如果回答前一個問題是是的,它會重新初始化它那麼這也應該是)?

+0

這是編譯器相關的,但得到的答覆是肯定的這兩個問題 – 2012-01-28 23:12:50

回答

2

在一般情況下,是的,模板類通常每編譯他們是由編譯器遇到的時間。

+0

爲什麼不編譯器「商店」的某處新的類,當它遇到它,它使用的已編譯的類,而不是編譯一個新的?爲什麼不通過說類似_same類名和definition_錯誤,如果它多次創建新類型? – Samaursa 2012-01-28 23:21:31

+0

@Samaursa:不要引用我,但我覺得有些編譯器做的實際上是「高速緩存」模板實例,以支持先進的[模板元編程(http://en.wikipedia.org/wiki/Template_metaprogramming)技術。不過,我認爲編譯器會檢查是否違反了這個定義規則。 – 2012-01-28 23:35:51

+1

鏈接器將刪除除了每個實例的一個副本。但是編譯器不知道你會鏈接哪些其他模塊,所以它必須編譯你使用的任何實例,除非你提供了在鏈接時使用'extern'提供的承諾。模板,如內聯函數,可以免除多重定義衝突;鏈接器只會選擇一個副本來使用。 ODR規則仍然適用,所有具有相同名稱的副本必須具有相同的行爲,以便鏈接程序選擇哪一個並不重要。 – 2012-01-28 23:36:07

3

基本上,每個編譯單元都會實例化模板,這會增加編譯時間。新C++標準中有一些擴展和功能可以處理這個問題,如顯式實例化和外部化。請參閱本文檔的一些解釋和優化技術:

http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html

+1

對於每個編譯單元和每個模板參數集,它們都被編譯*,這增加了編譯時間。不同參數的實例化可能會使二進制文件膨脹。但是我從來沒有聽說過由於多個編譯單元多次出現在二進制文件中的相同的實例化。 – 2012-01-28 23:34:34

+0

是的,簽名會以其他方式相撞。我想指出,編譯器本身無法知道實際使用哪個實例,因此,所有實例都是按照編譯單元創建/引用的。我最近遇到了一個共享庫,它太大了。靜態鏈接爲我做了。 – Sam 2012-01-29 00:04:40

+0

但「膨脹你的二進制」部分是誤導。 – 2012-01-29 00:05:38

1

編譯器如何實現模板實例化機制要由編譯器。實際上,當在翻譯單元中使用所需和可見的函數模板定義時,會創建它們。這可能會造成相當多的不必要的工作。例如,每次在翻譯單元中使用IOstream時,都會實例化流和語言環境類中的所有對應函數。 ...總是以相同的兩種類型!

根據模板是什麼,可能是可行把落實到一個源文件,而不是成頭和顯式實例源的模板。當有很多不同的實例被使用時,這是不可行的。在這種情況下,預先定義常用實例可能是合理的。爲此,您可以在C++ 2011中聲明實例化爲extern。當然,你需要在某個地方顯式實例化相應的函數或類。

1

是的,編譯單元是相互獨立編譯的。

然而,許多工具鏈支持哪規避這一點,並允許編譯器在另一個編譯單元重用已經處理的代碼預編譯頭。