2012-12-24 35 views
3

這是C++ 11中extern模板的正確用法嗎? (也可以是,extern template class和各自template class是在同一個翻譯單元可見?)模板類和相同的翻譯單元中的各自的extern模板類

// example.hpp: 
#pragma once 
template< typename T > 
class C { 
    void f(T); 
}; 
// question is about the next two lines 
extern template class C<float>; 
extern template class C<double>; 
// example_def.hpp: 
#include "example.hpp" 
template< typename T > 
void C<T>::f(T) { 
    //... smth. practicable 
} 
// example.cpp: 
#include "example_def.hpp" 
template class C<float>; 
template class C<double>; 
// other.hpp: 
#pragma once 
void g(); 
// other.cpp: 
#include "other.hpp" 
#include "example.hpp" 
// maybe those two lines should be here instead? 
void g() { 
    C<float>(); 
    C<double>(); 
} 
// main.cpp: 
#include "example.hpp" 
#include "other.hpp" 
// ...and here? 
int main() { 
    C<float>(); 
    C<double>(); 
    g(); 
    return 0; 
} 

回答

4

是的,既是extern template class規範(稱爲顯式實例聲明由標準)和template class規範(所謂顯式實例定義由標準)可以在同一個翻譯單元,如果定義(沒有extern)如下聲明(與extern):

(§14.7.2/ 11)如果一個實體既是一個顯式實例聲明,並在同一個翻譯單元的顯式實例去網絡nition的主題,去網絡nition應遵循的聲明。作爲明確實例化聲明主體的實體,也以某種方式使用,否則會導致翻譯單元中的隱式實例化(14.7.1),這些實體應該是程序中某處顯式實例化定義的主體;否則程序不合格,不需要診斷。 [注意:即使這樣一個實體的顯式實例化聲明沒有其他規範性的作用,這個規則也適用於內聯函數。這是爲了確保如果內嵌函數的地址在翻譯單元中被執行,在翻譯單元中實現選擇了抑制外線體,另一個翻譯單元將提供該內容。 - 結束註釋]顯式實例化聲明不應命名具有內部鏈接的模板的專門化。

(Emphasis mine)。術語顯式實例聲明顯式實例定義這裏定義:

(§14.7.2/ 2)爲顯式實例的語法是:

顯式實例:
extern opttemplate聲明

有兩種形式的ex plicit實例化:一個顯式實例化定義和一個顯式實例化聲明。顯式實例化聲明以extern關鍵字開頭。


這些明確的實例的效果如下:

  1. 的顯式實例聲明(與extern)阻止所有實例生效(除內聯函數和類模板專業,§14.7.2/ 10)。

  2. 的顯式實例的定義(沒有extern)導致的實例發生不管什麼,即它覆蓋了顯式實例聲明(這也從§14.7.2/ 10以下)。


一般評論
您顯式實例聲明位於定義模板意味着誰依次包括頭文件使用的頭文件的事實模板將不得不添加明確的實例定義,或者可替代地,需要鏈接到包括這樣的顯式實例化定義的另一.cpp文件的代碼。

這可能會令人困惑,並且當您希望許多不同的用戶爲多種不同類型實例化模板時,這可能不是一個好主意。但是,如果不同類型的實例化數量很少,並且您可以預期它們全部,那麼這可能是明智的。當然,您必須確保有一個(或幾個)文件包含所有實例化所需的明確實例化定義,並且其相應的目標文件在構建時與項目鏈接。

3

extern模板的基本思想是支持常用實例的顯式實例化,同時還支持不常用參數的隱式實例化。例如,std::basic_string<char>可以顯式實例化,但std::basic_string<signed char>可以留給隱式實例化(實際的激勵示例是需要大量時間來實例化的IOStreams,但實際只使用兩個實例化)。

爲了允許隱式實例化,使用模板的定義需要在使用模板的每個翻譯單元中可見。如果模板定義可見,則編譯器默認默認情況下需要隱式提供實例化。使用extern模板聲明告訴編譯器特定的模板實例化將由某個翻譯單元提供。

儘管您的案例有效,但甚至沒有必要聲明extern模板:當編譯器使用實例但未找到其定義時,它會假定實例在某個翻譯單元中找到。

+0

最後一段包含的信息不明顯。你確定這是事實嗎? – Orient

+0

@Dukales:我不完全確定你具體提到哪些信息,但我相信最後一段是正確的。你可以聲明一個模板化的函數,而不是爲編譯器提供一個隱式實例化的定義,並且可以通過在定義可見的翻譯單元中顯式實例化來解析結果未解析的符號(我認爲它們甚至可以通過隱式實例化來自另一個翻譯單元,但我認爲在這種情況下依靠隱式實例太脆弱)。 –