【发布时间】:2014-10-15 22:01:47
【问题描述】:
因此,我和一位同事一直在讨论显式模板实例化在减少编译时间、将声明与定义分开以及不影响我编写的用于其他项目的 C++ 数学库的性能方面的好处。
本质上,我有一个有用的数学函数库,旨在与 Vector3、Vector4、Quaternion 等原语一起使用。所有这些函数都用于模板参数为浮点数或双精度(在某些情况下为整数) .
这样我就不必编写这些函数两次,一次用于浮点数一次用于双精度,函数实现是模板化的,如下所示:
template<typename T>
Vector3<T> foo(const Vector4<T>& a,
const Quaternion<T>& b)
{ do something... }
所有定义在 .h 文件中(因此它们被隐式标记为内联)。这些函数大部分都很短,希望在使用编译期间内联。
尽管头文件变得相当大,编译时间也在增加,而且仅通过查看头文件就很难找到函数的存在(这是我喜欢将声明与实现分开的众多原因之一)。
所以我可以在随附的 .cpp 文件中使用显式模板实例化,如下所示:
//in .h
template<typename T>
Vector3<T> foo(const Vector4<T>& a,
const Quaternion<T>& b)
{ do something... }
//in .cpp
template Vector3<float> foo<float>(const Vector4<float>& a,
const Quaternion<float>& b);
template Vector3<double> foo<double>(const Vector4<double>& a,
const Quaternion<double>& b);
这应该有助于编译时间吗? 这会影响函数被内联的可能性吗? 这些问题的答案通常是编译器特定的吗?
一个额外的好处是它会验证函数是否可以编译,即使我还没有使用它。
我也可以这样做:
//in .h
template<typename T>
Vector3<T> foo(const Vector4<T>& a,
const Quaternion<T>& b);
//in .cpp
template<typename T>
Vector3<T> foo(const Vector4<T>& a,
const Quaternion<T>& b)
{ do something... }
template Vector3<float> foo<float>(const Vector4<float>& a,
const Quaternion<float>& b);
template Vector3<double> foo<double>(const Vector4<double>& a,
const Quaternion<double>& b);
该方法的相同问题:
这应该有助于编译时间吗? 这会影响函数被内联的可能性吗? 这些问题的答案通常是编译器特定的吗?
考虑到定义不在标题中,我预计内联的可能性肯定会受到影响。
很高兴它设法将模板函数的声明和定义(针对特定模板参数)分开,而无需使用 .h 文件底部包含的 .inl 之类的操作。这也对库的用户隐藏了实现,这是有益的(但还不是绝对必要的),同时仍然能够使用模板,所以我不必实现一个函数 N 次。
有没有办法通过调整方法来允许内联?
我发现仅仅在谷歌上搜索这些问题的答案很困难,而且这些主题的标准规范也很难理解(至少对我而言)。
顺便说一句,这有望与 VS2010、VS2012 和 GCC 4.7 一起编译。
我们将不胜感激。
谢谢
【问题讨论】:
-
我不认为有很多编译器允许头文件之外的模板...
-
@MatsPetersson:这是我一整天听到的最令人费解的说法。
-
@MatsPetersson 这不是真的,常见的编译器没有“头文件”的概念。一旦你
#includecpp 文件中的头文件,就好像头文件是cpp 文件的一部分。您可能的意思是,模板方法/类的定义必须在当前翻译单元中可用,如果它被实例化为未在任何其他翻译单元中实例化的类型(这个将获得的目标文件链接到)。但是,如果您为您需要的所有类型(作为 OP)明确地将它们实例化为一个,则只需要头文件中的声明。 -
好吧,我也许应该说,“我不认为有很多编译器允许模板的链接时解析”。换句话说,您需要将模板的源代码放在与它所使用的翻译单元相同的翻译单元中。当然,您可以在不是头文件的东西中拥有模板,但前提是您也在那里使用它。还是我错过了什么?
-
实际上,我认为,如果不是全部的话,他们中的大多数都会执行您所说的“链接时间解析”。这就是为什么您会收到链接器错误,如果模板实例化的代码不在您链接的任何目标文件中,而不是编译器错误。实际上,我认为,您是在倒退考虑这一点:通常,我们有一个定义规则。对于模板,它已经放宽了(例如,对于外部内联函数),因为您永远不知道其他翻译单元中需要哪些实例化,这会破坏模板的目的。但是,如果您选择这样做,您仍然可以遵守模板规则。