Visual C++中的外部模板是否存在错误

Is there a bug with extern template in Visual C++?

本文关键字:是否 存在 错误 外部 C++ Visual      更新时间:2023-10-16

给定此代码:

//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>;

根据这篇博客文章,extern模板从VC10开始就应该得到支持。http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

顺便说一句,有没有一种方法可以在Windows/ViewStudio上列出对象文件中的名称?在Linux上我会做:

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

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))
^^^^^