6

鉴于此代码:

//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),这确实可以编译和链接。在 GCC 中,它不会:

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

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

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

根据这篇博文,从 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
4

1 回答 1

16

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)时仍将被隐式实例化,以便可以考虑将主体内联,但没有外联副本的内联函数将在翻译单元中生成。——尾注]

查看内联 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))
             ^^^^^
...

使用 ctor 内联编译示例:

00A 00000000 SECT4  notype ()    External     | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
             ^^^^^
于 2012-09-29T07:49:20.863 回答