Storing C++ template function definitions in a .CPP file
我有一些模板代码希望存储在cpp文件中,而不是在头中内联。我知道只要您知道将使用哪种模板类型,就可以这样做。例如:
h文件
1 2 3 4 5 6 | class foo { public: template <typename T> void do(const T& t); }; |
.CPP文件
1 2 3 4 5 6 7 8 | 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中使用此代码,但希望将其移植到其他环境。
您描述的问题可以通过在标题中定义模板来解决,或者通过您上面描述的方法来解决。
我建议从C++ FAQ Lite中阅读以下几点:
- 为什么我不能从模板类的声明中分离模板类的定义并将其放入.cpp文件中?
- 如何避免模板函数出现链接器错误?
- C++关键字导出如何帮助模板链接器错误?
他们详细介绍了这些(和其他)模板问题。
对于本页上的其他人,如果想知道显式模板专门化(或者至少在VS2008中)的正确语法是什么(和我一样),请参阅以下内容…
在你的.h文件中…
1 2 3 4 5 6 | template<typename T> class foo { public: void bar(const T &t); }; |
在你的.cpp文件中
1 2 3 4 5 6 | template <class T> void foo<T>::bar(const T &t) { } // Explicit template instantiation template class foo<int>; |
此代码格式正确。您只需注意模板的定义在实例化时是可见的。引用标准第14.7.2.4条:
The definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.
这在支持模板的任何地方都可以很好地工作。显式模板实例化是C++标准的一部分。
您的示例是正确的,但不是非常可移植的。还有一个稍微干净一点的语法可以使用(如@namespace sid所指出的)。
假设模板类是要共享的某个库的一部分。是否应编译模板类的其他版本?库维护人员是否应该预测类的所有可能的模板化使用?
另一种方法是对您所拥有的内容进行细微的更改:添加第三个文件,即模板实现/实例化文件。
fo.h文件
1 2 3 4 5 6 7 8 | // Standard header file guards omitted template <typename T> class foo { public: void bar(const T& t); }; |
文件文件
1 2 3 4 5 6 7 8 | // Always include your headers #include"foo.h" template <typename T> void foo::bar(const T& t) { // Do something with t } |
foo-impl.cpp文件
1 2 3 | // Yes, we include the .cpp file #include"foo.cpp" template class foo<int>; |
一个警告是,您需要告诉编译器编译
当然,您可以在第三个文件中有多个实现,或者对于您想要使用的每种类型有多个实现文件。
当共享模板类以供其他用途时,这使得灵活性大大提高。
这个设置还减少了重用类的编译时间,因为您没有在每个翻译单元中重新编译相同的头文件。
这绝对不是一个令人讨厌的黑客,但要注意的是,对于您想要与给定模板一起使用的每一个类/类型,都必须这样做(显式模板专门化)。在许多类型请求模板实例化的情况下,.cpp文件中可能有很多行。为了解决这个问题,您可以在您使用的每个项目中都有一个templateclassinst.cpp,这样您就可以更好地控制将要实例化的类型。显然,这个解决方案不会是完美的(也就是银弹),因为你可能最终会打破ODR:。
在最新的标准中,有一个关键字(
请参阅有关此问题的FAQ Lite。
是的,这是执行专门化的标准方法显式实例化。如您所述,不能用其他类型实例化此模板。
编辑:根据注释更正。
这是定义模板函数的标准方法。我认为有三种定义模板的方法。或者可能是4。各有利弊。
在类定义中定义。我一点也不喜欢这样,因为我认为类定义是严格的参考,应该易于阅读。然而,在类中定义模板比在外部要容易得多。并不是所有的模板声明都在相同的复杂程度上。此方法还使模板成为真正的模板。
在同一个头中定义模板,但在类之外。大多数时候这是我最喜欢的方式。它保持类定义整洁,模板仍然是一个真正的模板。但是,它需要完整的模板命名,这可能很棘手。而且,您的代码对所有人都可用。但是,如果需要将代码内联,这是唯一的方法。您还可以通过在类定义的末尾创建一个.inl文件来实现这一点。
在main.cpp中包含header.h和implementation.cpp。我想是这样的。您不必准备任何预实例化,它的行为就像一个真正的模板。我有一个问题,那是不自然的。我们通常不包括并希望包括源文件。我想既然您包含了源文件,模板函数就可以被内联。
最后一个方法,即发布的方法,是在源文件中定义模板,就像数字3一样;但是我们不包括源文件,而是将模板预实例化为我们需要的模板。我对这个方法没有问题,有时它也很有用。我们有一个大代码,它不能从内联中受益,所以只需将它放在一个cpp文件中。如果我们知道常见的实例化,我们可以预先定义它们。这就避免了我们写5,10次基本相同的东西。这种方法的好处是保持我们的代码是专有的。但我不建议在cpp文件中放置小的、经常使用的函数。因为这样会降低库的性能。
注意,我不知道obj文件膨胀的后果。
该更新了!创建一个内联(.inl,或任何其他)文件,并简单地复制其中的所有定义。一定要在每个函数(
我真的不知道为什么没有人提到这个。我看不出有什么直接的缺点。
你给出的例子没有错。但我必须说,我相信在cpp文件中存储函数定义是不有效的。我只理解分离函数声明和定义的需要。
当与显式类实例化一起使用时,boost概念检查库(bccl)可以帮助您在cpp文件中生成模板函数代码。