一個類一般都是在頭文件(.h)中聲明,在源文件(.cpp)中實現,但是在使用模板的時候,如果將模板成員函數分別放在頭文件和源文件中則編譯時會出現錯誤。錯誤原因是找不到鏈接。因爲當實例化一個模板時,編譯器必須看到模板確切的定義,而不僅僅是它的聲明。因此,最好的辦法就是將模板的聲明和定義都放置在同一個頭文件(.h)中。這就是爲什麼所有的STL頭文件都包含模板定義的原因。當然如果一定要在源文件(.cpp)中定義則在編譯器支持的情況下可使用“export”關鍵字,即在模板成員函數的定義前面加上“export”,很遺憾的是visual studio和GCC是不支持此關鍵字,因此最好還是將聲明和定義都寫到頭文件(.h)中。
分離編譯模式(Separate Compilation Model)允許在一處翻譯單元(Translation Unit)中定義(define)函數、類型、類對象等,在另一處翻譯單元引用它們。編譯器(Compiler)處理完所有翻譯單元后,鏈接器(Linker)接下來處理所有指向 extern 符號的引用,從而生成單一可執行文件。然而該模式卻馴不服模板(Template)。標準要求編譯器在實例化模板時必須在上下文中可以查看到其定義實體;而反過來,在看到實例化模板之前,編譯器對模板的定義體是不處理的——原因很簡單,編譯器怎麼會預先知道 typename 實參是什麼呢?因此模板的實例化與定義體必須放到同一翻譯單元中。
例:
templateClass.h
class TemplateClass
{
public:
TemplateClass() {}
~TemplateClass() {}
template<typename T>
T sum(T t1, T t2);
template<typename S>
void print(S s);
private:
};
template<typename T>
T TemplateClass::sum(T t1, T t2)
{
T s = t1 + t2;
cout << "sum(): " << s << endl;
return s;
}
template<typename S>
void TemplateClass::print(S s)
{
cout << "print(): "<< s << endl;
}
main.cpp
int main()
{
TemplateClass tem;
tem.sum(1.5, 2.6); // 自動調用,編譯器根據傳入的值來推導類型
tem.sum<int>(123, 268); // 顯示調用,告訴編譯器調用的是int類型
tem.print("aaa");
tem.print(123);
system("pause");
return 0;
}
輸出: