16.3 模板編譯模型
當編譯器看到模板定義的時候,它不立即產生代碼。只有在看到用到模板時,如果調用了函數模板或定義了類模板的對象的時候,編譯器才產生特定類型的模板實例。
應該將類定義和函數聲明放在頭文件中,而普通函數和類成員函數的定義放在源文件中。
模板則不同:要進行實例化,編譯器必須能夠訪問定義模板的源代碼。當調用函數模板或類模板的成員函數的時候,編譯器需要函數的定義,需要那些通常放在源文件中的代碼。
標准C++為編譯模板代碼定義了兩種模型。在兩種模型中,構造程序的方式很大程度上是相同的:類定義和函數聲明放在頭文件中,而函數定義和成員定義放在源文件中。兩種模型的不同在於,編譯器怎樣使用來自源文件的定義。
1. 包含編譯模型
在包含編譯模型(inclusion compilation model)中,編譯器必須看到用到的所有模板的定義。一般而言,可以通過聲明函數模板或類模板的頭文件中添加一條#include指示使定義可用,該#include引入了包含相關文件的源文件。
//Template1.h
#ifndef TEMPLATE1_H
#define TEMPLATE1_H
template <class T>
int compare(const T&, const T&);
#include "Template1.cpp"
#endif
//Template1.h
#ifndef TEMPLATE1_H
#define TEMPLATE1_H
template <class T>
int compare(const T&, const T&);
#include "Template1.cpp"
#endif//Template1.cpp
#include "stdafx.h"
template <class T>
int compare(const T &v1,const T &v2)
{
if(v1<v2) return -1;
else if(v2<v1) return 1;
else return 0;
}
//Template1.cpp
#include "stdafx.h"
template <class T>
int compare(const T &v1,const T &v2)
{
if(v1<v2) return -1;
else if(v2<v1) return 1;
else return 0;
}cout<<compare(100,200)<<endl;
cout<<compare(100,200)<<endl;2. 分別編譯模型
在分別編譯模型(separate compilation model)中,編譯器會為我們跟蹤相關的模板定義。但是,我們必須讓編譯器知道要記住給定的模板定義,可以使用export關鍵字(export keyword)來做這件事。
export關鍵字能夠指明給定的定義可能會需要在其他文件中產生實例化。在一個程序中,一個模板只能定義為導出一次。編譯器在需要產生這些實例化時計算出怎樣定位模板定義。export關鍵字不必在模板聲明中出現。
一般我們在函數模板的定義中指明函數模板是導出的,這是通過在關鍵字template之前包含export關鍵字而實現的。
函數模板的聲明通常應放在頭文件中,聲明不必指定export。
通常,類聲明必須放在頭文件中,頭文件中的類定義體不應該使用關鍵字export,如果頭文件中使用了export,則該頭文件中只能被程序中的一個源文件使用。
相反,應該在類的實現文件中使用export。
摘自 xufei96的專欄