Visual C++ 中的 extern 模板存在漏洞吗?

7

给定以下代码:

//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 template。 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
1个回答

17

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-used(3.2)时仍将被隐式实例化,以便可以考虑其主体进行内联,但不会在翻译单元中生成内联函数的out-of-line副本。- end note]

当将ctor内联时查看所创建的汇编代码,即使在使用优化编译示例时永远不会调用ctor,也会在目标文件中放置ctor的out-of-line副本,因此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))
             ^^^^^
...

将带有内联构造函数的示例编译:

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

啊,内联,我没想到那个。这就解释了。 - knatten

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接