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
的定义(extern template
应该防止这种情况发生).使用 VC++ 11 (Visual Studio 2012),这会编译和链接.在 GCC 中,它不会:
By my understanding, this program should not link, since there should be no definition of class Foo<int>
anywhere (extern template
should prevent this). With VC++ 11 (Visual Studio 2012), this does however compile and link. In GCC, it doesn't:
source1.cpp:(.text+0x15): undefined reference to `Foo<int>::Foo(int)'
但是,如果我与 source2.cpp 链接,它会起作用(正如我所期望的那样):
If I link with source2.cpp however, it works (as I expect i should) :
#include "header.h"
template class Foo<int>;
根据这篇博文,从 VC10 开始应该支持 extern 模板.http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
According to this blog post, extern template should have been supported since VC10. http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
顺便提一下,有没有办法在 Windows/Visual Studio 上列出目标文件中的名称?在 Linux 上我会这样做:
On a side note, is there a way to list the names in an object file on Windows / Visual Studio? On Linux I would do:
$ nm source1.o
U _ZN3FooIiEC1Ei <- "U" means that this symbol is undefined.
0000000000000000 T main
推荐答案
C++11 14.7.2/10 显式实例化"说:
C++11 14.7.2/10 "Explicit instantiation" says:
除了内联函数和类模板特化,显式实例化声明具有抑制它们引用的实体的隐式实例化.
Except for inline functions and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer.
你类模板中的构造函数Foo
是内联的.如果您像这样构造标题,VS2012 将按照您期望的方式工作:
And the constructor in you class template Foo<T>
is inline. VS2012 will work the way that you expect if you structure the header like so:
//header.h
template <class T>
class Foo
{
public:
Foo(T t);
T t;
};
template <class T>
Foo<T>::Foo(T t) : t(t)
{
}
这样构造函数就不是内联的.
so that the constructor isn't inline.
我上面引用的标准中的段落确实包括以下注释:
The paragraph from the standard I quoted above does include the following note:
[ 注意:意图是作为主题的内联函数显式实例化声明仍将是隐式的使用 odr 时实例化 (3.2) 以便可以考虑主体用于内联,但没有内联函数的外联副本将在翻译单元中生成.― 尾注 ]
[ Note: The intent is that an inline function that is the subject of an explicit instantiation declaration will still be implicitly instantiated when odr-used (3.2) so that the body can be considered for inlining, but that no out-of-line copy of the inline function would be generated in the translation unit. ― end note ]
查看内联构造函数时创建的汇编代码,构造函数的外联副本放置在目标文件中(即使编译示例并进行优化时,构造函数甚至从未被调用),所以 MSVC 似乎没有遵循标准的意图.但是,注释不规范,所以我认为MSVC的行为是符合的.
Looking at the assembly code created when the ctor is inlined, an out-of-line copy of the ctor is placed in the object file (even though the ctor is never even called if you compile the example with optimizations on), so MSVC doesn't appear to be following the intent of the standard. However, notes are not normative, so I believe that MSVC's behavior is conforming.
关于从使用 MSVC 构建的目标文件中转储符号的附带问题,您可以使用 dumpbin
实用程序:
Regarding your side question about dumping symbols from object files built with MSVC, you can use the dumpbin
utility:
使用非内联构造函数编译示例时:
When compiling the example with the non-inline constructor:
dumpbin /symbols test.obj
...
008 00000000 UNDEF notype () External | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
^^^^^
...
使用内联的ctor编译示例:
Compiling the example with the ctor inlined:
00A 00000000 SECT4 notype () External | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
^^^^^
相关文章