2011-08-20 80 views
1

我剛剛花了大約20分鐘的時間試圖弄清楚爲什麼我的一些模板方法通過編譯而不是鏈接。關於C++模板和顯式聲明

原來我需要顯式聲明我的模板方法。

正是這種東西:

class Test { 
    template<class Source> void Save(Source& obj); 
}; 

然後我會用它像這樣的地方:

Test t; 
ClassDerivedFromInterface obj; 
t.Save(obj); 

它編譯罰款,但沒有鏈接。直到我補充說:

template void Test::Save(ClassDerivedFromInterface); 

我想明白在這種情況下,顯式聲明是必要的。

感謝

+1

請提供真實的代碼。 –

+0

是't.Save(obj);'和'template void Test :: Save(ClassDerivedFromInterface);'在同一個文件中? –

回答

5

簡而言之,您需要讓模板函數的整個主體(定義)對實例化模板的翻譯單元可見。因此,當您說t.Save(obj);時,該翻譯單位應該有權訪問Save的定義。通常你通過在頭文件本身中包含函數模板的定義來實現這一點。

其原因是模板不是普通的代碼,它可以被編譯並隨後可以隨意鏈接。相反,模板是代碼生成工具,可以根據需要生成必要的代碼 - 如果您願意,可以使用自動版本的複製/粘貼,然後搜索並替換。

因此,您的函數Save(ClassDerivedFromInterface&)的實際可編譯代碼在您寫入該行之前不會生成。如果只有函數模板的聲明是可見的,那麼模板只會生成具體函數的聲明,而不會生成它的主體,因此在鏈接時注意到函數缺失。要回顧一下,模板本身不能被編譯,它只是它們的具體實例,你必須注意確保具體實例在你實例化時總是可用的。顯式實例化就像它有效並允許將幾個特定實例打包到單獨的TU中,但通常很難維護並且不可伸縮,並且顯式實例化還有其他缺點,您可以在隱式地實例化編譯器時避免這些缺點。所以通常最好將整個定義打包到頭文件中。

+0

非常感謝!現在我對模板的工作原理有了一個瞭解,現在我覺得這很正常。 – lollancf37

2

您將需要顯式聲明模板,如果模板源不是在編譯時可見。此鏈接涵蓋它不錯,也一般一個真棒網站:

C++ FAQ 35.13

2

你需要在你的模板定義不是從使用它們的代碼入店明確聲明,考慮以下因素:

template.h - template declarations 
template.cpp - template definitions 
main.cpp - template usage 

template.cpp未包含在main.cpp中,因此模板用戶無法訪問,因此您需要顯式聲明。
但是如果你的結構是:

template.h - template declarations and definitions 
main.cpp - template usage 

模板聲明是由模板用戶可到達的,所以你不需要明確的聲明。

+0

請注意有一個「導出」關鍵字折舊。 – xis

1

編譯器需要知道你將使用哪種類型的模板。如果您創建了一個模板類,然後將其用於例如int,chardouble,那麼編譯器將爲這些類型的模板創建方法。如果您在使用它的單獨編譯單元中編譯模板方法,那麼編譯器將不會爲您需要的類型實例化您的模板。但是如果你明確地實例化模板,編譯器會創建你告訴它的任何東西。