在Visual C++中是否有extern模板的错误?

kna*_*ten 5 c++ visual-c++ c++11 visual-studio-2012

鉴于此代码:

//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);
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这个程序不应该链接,因为class Foo<int>任何地方都不应该有定义(extern template应该防止这个).使用VC++ 11(Visual Studio 2012),无论如何编译和链接.在海湾合作委员会,它没有:

source1.cpp:(.text+0x15): undefined reference to `Foo<int>::Foo(int)'
Run Code Online (Sandbox Code Playgroud)

如果我链接到source2.cpp但是它可以工作(正如我所期望的那样):

#include "header.h"
template class Foo<int>;
Run Code Online (Sandbox Code Playgroud)

根据这篇博文,自VC10以来应该支持extern模板. 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
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 15

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) 
{
}
Run Code Online (Sandbox Code Playgroud)

这样构造函数就不是内联的.

我在上面引用的标准中的段落确实包括以下注释:

[注意:意图是当使用odr(3.2)时,仍然会隐式实例化作为显式实例化声明主题的内联函数,以便可以考虑使用内联体,但是不需要外联副本内联函数的内容将在翻译单元中生成. - 结束说明]

查看内联ctor时创建的汇编代码,将ctor的外联副本放在目标文件中(即使在编译优化项的示例时甚至不调用ctor),因此MSVC不会似乎遵循标准的意图.但是,注释不是规范性的,所以我认为MSVC的行为是符合的.


关于从使用MSVC构建的目标文件转储符号的问题,您可以使用该dumpbin实用程序:

使用非内联构造函数编译示例时:

dumpbin /symbols test.obj

...

008 00000000 UNDEF  notype ()    External     | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
             ^^^^^
...
Run Code Online (Sandbox Code Playgroud)

使用内联的ctor编译示例:

00A 00000000 SECT4  notype ()    External     | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
             ^^^^^
Run Code Online (Sandbox Code Playgroud)