2012-09-29 49 views
5

鑑於這種代碼:在Visual C++中是否存在extern模板的錯誤?

//header.h 
template <class T> 
class Foo 
{ 
public: 
    Foo(T t) : t(t) {} 
    T t; 
}; 

//source1.cpp: 
#include "header.h" 
extern template class Foo<int>; 
int main() 
{ 
    Foo<int> f(42); 
} 

按照我的理解,這個程序應該沒有鏈接,因爲應該沒有的class Foo<int>任何地方定義(extern template應該避免這種情況)。使用VC++ 11(Visual Studio 2012),這不會編譯和鏈接。在海灣合作委員會,它不:

source1.cpp:(.text+0x15): undefined reference to `Foo<int>::Foo(int)' 

如果我然而source2.cpp鏈接,它的工作原理(如我期望我應該):

#include "header.h" 
template class Foo<int>; 

根據這一博客帖子,EXTERN模板應自VC10以來一直得到支持。 http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

在附註中,是否有辦法在Windows/Visual Studio上的目標文件中列出名稱?在Linux上,我會做:

$ nm source1.o 
U _ZN3FooIiEC1Ei  <- "U" means that this symbol is undefined. 
0000000000000000 T main 

回答

14

C++ 11 14.7.2/10 「顯式實例化」 說:

除了內聯函數和類模板特, 顯式實例聲明有效果抑制它們引用的實體的隱式實例化。

而你的類構造函數模板Foo<T>是內聯的。 VS2012會工作,如果你結構中的頭一樣,讓你期望的方式:

//header.h 
template <class T> 
class Foo 
{ 
public: 
    Foo(T t); 
    T t; 
}; 

template <class T> 
Foo<T>::Foo(T t) : t(t) 
{ 
} 

,這樣的構造不在線。

從我上面引述不包括下面的註釋標準段落:

[注:這樣做的目的是,內聯函數是 主體的顯式實例聲明仍然會隱含 實例當使用odr(3.2)時,可以將內容視爲 進行內聯,但不會在翻譯單元中生成內聯函數 的外聯副本。 - 注完]

望着當構造函數內聯創建的彙編代碼中,構造函數的亂線副本放置在目標文件(即使如果您編譯構造函數永遠不會甚至稱在在優化上的例子),所以MSVC似乎沒有遵循標準的意圖。但是,筆記不是規範的,所以我相信MSVC的行爲是一致的。


關於對傾銷的從MSVC內置目標文件的符號你身邊的問題,你可以使用dumpbin實用程序:

編譯與非內聯構造函數的例子當:

dumpbin /symbols test.obj 

... 

008 00000000 UNDEF notype() External  | [email protected]@@[email protected]@Z (public: __thiscall Foo<int>::Foo<int>(int)) 
      ^^^^^ 
... 

用內聯編譯示例:

00A 00000000 SECT4 notype() External  | [email protected]@@[email protected]@Z (public: __thiscall Foo<int>::Foo<int>(int)) 
      ^^^^^ 
+0

啊,inlinin克,我沒有想到這一點。這解釋了它。 – knatten