使用外部模板时链接器错误

Linker error when using an extern template

本文关键字:链接 错误 外部      更新时间:2023-10-16

我有一个模板的工作代码。与stl::string类似,我主要是在多个编译单元中使用带有一个参数的模板。为了节省时间,我试图使用外部实例化。然而,像下面这样更改行会产生一个错误。正确的做法是什么?(注:使用c++0x标志在gcc上编译)

typedef myTemplate_base<commonType> myTemplate;
extern template class myTemplate_base<commonType>; //using "extern template myTemplate" wont work

我在项目中添加了一个额外的cpp文件,其中包含以下内容。

template class myTemplate_base<commonType>;

链接器出现这个错误消息(给出主文件中第一个对象实例化(myTemplate someVar;)的行作为错误源):

未定义引用'myTemplate_base::~myTemplate_base()'

然而,该类型在类中具有以下定义~myTemplate() = default;

编辑:如果你有一个更好的标题,请评论,让合适的人看看这个

Edit2:有一个有趣的事情,template class myTemplate_base<commonType>的添加极大地增加了可执行文件的大小(在450k二进制文件上+100k),即使模板在主程序中使用(为了编译,我必须将extern部分注释出来)。这暗示链接器保持了具有相同实例化的模板的两个实现/me忽略了一些东西。

您发现了一个编译器错误。我用g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1复制了它。解决方法是将析构函数保留为隐式默认值。只有当析构函数显式地将其标记为默认(= default)时,才会出现链接器错误。

无论哪种方式,析构函数都不会与外部模板一起产生。将模板类标记为extern,编译器会注意到任何需要的符号都是extern。除了析构函数,它仍然在文件中定义。看起来,添加一个带有extern模板的= default会使编译器混淆,认为析构函数将在其他地方定义。

代码膨胀是由外部模板引起的。编译器只实例化模板类中实际使用的方法。这通常比定义的数字少得多。当你使用extern template强制实例化一个类时,编译器会发出所有方法的代码(除了刚才发现的析构函数)。


g++ -c -std=c++0x main.cpp
g++ -c -std=c++0x extern.cpp
g++ main.o extern.o

header.hpp

#pragma once
#include <iostream>
#include <string>
#include <typeinfo>
template< typename T >
class Foo
{
public:
   Foo( void ) :
      m_name( typeid(T).name() ),
      m_t( T() )
   { }
   // add or remove this to cause the error
   ~Foo( void ) = default;
   void printer( void )
   {
      std::cout << m_name << std::endl;
   }
   T returner( void )
   {
      return m_t;;
   }
private:
   std::string m_name;
   T m_t;
};
extern template class Foo<int>;

extern.cpp

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

main.cpp

#include "header.hpp"
int main()
{
   Foo<int> fi;
   fi.printer();
   Foo<float> ff;
   ff.printer();
}