我有带模板的工作代码。与 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;
) 的行作为错误源):
undefined reference 'myTemplate_base::~myTemplate_base()'
但是这个类型在具有以下定义的类中 ~myTemplate() = default;
编辑:如果您有更好的标题请发表评论,以便合适的人看一看
Edit2:有趣的是,添加了template class myTemplate_base<commonType>
。极大地增加了可执行文件的大小(在 450k 的二进制文件上增加了 100k),即使在主文件中使用了模板(为了编译我必须注释掉 extern
部分)。这暗示链接器保留了具有相同实例化的模板的两个实现/我忽略了某些东西。
最佳答案
您发现了一个编译器错误。我用 g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
复制了它。解决方法是将析构函数保留为隐式默认值。仅当析构函数被显式标记为默认 (= default
) 时才会出现链接器错误。
无论哪种方式,析构函数都不会使用 extern 模板生成。将模板类标记为 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
标题.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>;
外部.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();
}
关于c++ - 使用外部模板时出现链接器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8560839/