将C ++模板函数定义存储在.CPP文件中
我有一些模板代码,我宁愿保存在一个CPP文件,而不是内联的头。 我知道这可以做,只要你知道哪些模板types将被使用。 例如:
.h文件
class foo { public: template <typename T> void do(const T& t); };
.cpp文件
template <typename T> void foo::do(const T& t) { // Do something with t } template void foo::do<int>(const int&); template void foo::do<std::string>(const std::string&);
请注意最后两行–foo :: do模板函数仅用于ints和std :: strings,所以这些定义意味着应用程序将链接。
我的问题是 – 这是一个讨厌的黑客,还是会与其他编译器/连接器? 我现在只在VS2008上使用这个代码,但是会想要移植到其他环境中。
您所描述的问题可以通过在标题中定义模板或者通过上面描述的方法来解决。
我build议阅读C ++ FAQ Lite中的以下几点:
- 为什么我不能将我的模板类的定义从它的声明中分离出来并放在一个.cpp文件中?
- 如何避免使用我的模板函数的链接器错误?
- C ++关键字导出如何帮助模板链接器错误?
他们对这些(和其他)模板问题进行了很多细节。
对于在这个页面上的其他人想知道什么是正确的语法(如我)显式模板专业化(或至less在VS2008),其以下…
在你的.h文件中
template<typename T> class foo { public: void bar(const T &t); };
并在您的.cpp文件
template <class T> void foo<T>::bar(const T &t) { } // Explicit template instantiation template class foo<int>;
此代码格式良好。 你只需要注意,模板的定义在实例化的时候是可见的。 引用标准§14.7.2.4:
非导出函数模板,非导出成员函数模板或类模板的非导出成员函数或静态数据成员的定义应存在于每个明确实例化的翻译单元中。
这应该在任何支持模板的地方都能正常工 显式模板实例化是C ++标准的一部分。
这绝对不是一个讨厌的黑客行为,但是要意识到你需要为给定的模板使用的每个类/types(显式模板特化)这样做。 万一要求模板实例化的types可能会在您的.cpp文件中有很多行。 为了解决这个问题,你可以在你使用的每个项目中都有一个TemplateClassInst.cpp,这样你就可以更好的控制什么types的实例化了。 显然这个解决scheme不会是完美的(又名银弹),因为你可能最终打破ODR :)。
在最新的标准中,有一个关键字( export
)可以帮助缓解这个问题,但是除了Comeau之外,并没有在我知道的任何编译器中实现。
请参阅关于此的FAQ-lite 。
你的例子是正确的,但不是很便携。 还有一个稍微干净的语法可以使用(正如@ namespace-sid指出的那样)。
假设模板类是要共享的某个库的一部分。 应该编译其他版本的模板类吗? 图书馆维护人员是否应该预见class级的所有可能的模板用途?
另一种方法是对所拥有的进行细微的变化:添加第三个文件,即模板实现/实例化文件。
foo.h文件
// Standard header file guards omitted template <typename T> class foo { public: void bar(const T& t); };
foo.cpp文件
// Always include your headers #include "foo.h" template <typename T> void foo::bar(const T& t) { // Do something with t }
foo-impl.cpp文件
// Yes, we include the .cpp file #include "foo.cpp" template class foo<int>;
一个需要注意的是,你需要告诉编译器编译foo-impl.cpp
而不是foo.cpp
因为编译后者什么也不做。
当然,你可以在第三个文件中有多个实现,或者为每个types使用多个实现文件。
当共享模板类用于其他用途时,这使得更多的灵活性。
此设置还可以减less重用类的编译时间,因为您不会在每个翻译单元中重新编译相同的头文件。
是的,这是做专门化显式实例化的标准方法。 正如你所说的,你不能用其他types实例化这个模板。
编辑:根据评论更正。
你提供的例子没有任何错误。 但是我必须说,我相信把函数定义存储在一个cpp文件是没有效率的。 我只理解需要将函数的声明和定义分开。
当与显式类实例化一起使用时,Boost概念检查库(BCCL)可以帮助您在cpp文件中生成模板函数代码。
时间更新! 创build一个内联(.inl或者其他任何文件),并简单地复制你的所有定义。 确保在每个函数( template <typename T, ...>
)上添加模板。 现在不是将头文件包含在内联文件中,而是做相反的事情。 在你的类声明之后join内联文件( #include "file.inl"
)。
我真的不知道为什么没有人提到这一点。 我看不到立即的缺点。