【问题标题】:Explicit template function instantiation with inlining带有内联的显式模板函数实例化
【发布时间】: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 这不是真的,常见的编译器没有“头文件”的概念。一旦你#include cpp 文件中的头文件,就好像头文件是cpp 文件的一部分。您可能的意思是,模板方法/类的定义必须在当前翻译单元中可用,如果它被实例化为未在任何其他翻译单元中实例化的类型(这个将获得的目标文件链接到)。但是,如果您为您需要的所有类型(作为 OP)明确地将它们实例化为一个,则只需要头文件中的声明。
  • 好吧,我也许应该说,“我不认为有很多编译器允许模板的链接时解析”。换句话说,您需要将模板的源代码放在与它所使用的翻译单元相同的翻译单元中。当然,您可以在不是头文件的东西中拥有模板,但前提是您也在那里使用它。还是我错过了什么?
  • 实际上,我认为,如果不是全部的话,他们中的大多数都会执行您所说的“链接时间解析”。这就是为什么您会收到链接器错误,如果模板实例化的代码不在您链接的任何目标文件中,而不是编译器错误。实际上,我认为,您是在倒退考虑这一点:通常,我们有一个定义规则。对于模板,它已经放宽了(例如,对于外部内联函数),因为您永远不知道其他翻译单元中需要哪些实例化,这会破坏模板的目的。但是,如果您选择这样做,您仍然可以遵守模板规则。

标签: c++ templates


【解决方案1】:

我假设您的技术与此问题的答案相同:Template instantiation effect on compile duration

为了达到预期的结果,您还需要通过使用extern 在标头中声明显式实例化来防止自动实例化。见Explicit instantiation declaration with extern

//in .h
template<typename T>
Vector3<T> foo(const Vector4<T>& a, 
               const Quaternion<T>& b);

extern template Vector3<float> foo<float>(const Vector4<float>& a, 
                                          const Quaternion<float>& b);

extern template Vector3<double> foo<double>(const Vector4<double>& a, 
                                            const Quaternion<double>& 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);

这应该有助于编译时间吗?这会影响函数被内联的可能性吗?这些问题的答案通常是编译器特定的吗?

答案在很大程度上取决于编译器 - 并且应该更准确地根据经验确定 - 但我们可以对其进行概括。

我们可以假设编译时间的增加不是来自解析额外的模板尖括号语法的成本,而是来自模板实例化(复杂)过程的成本。如果是这种情况,那么在多个翻译单元中使用给定模板特化的成本应该会显着增加编译时间,前提是实例化成本很高并且编译器会多次执行实例化。

C++ 标准隐式允许编译器在所有翻译单元中只对每个唯一模板特化执行一次实例化。也就是说,模板函数的实例化可以延迟并在初始编译之后执行,如Comeau 文档中所述。这种优化是否实现取决于编译器,但肯定不会在 2015 年之前的任何版本的 MSVC 中实现。

如果您的编译器在链接时执行实例化,如果编译器不支持跨模块内联,则此技术将阻止内联。较新版本的 MSVC、GCC 和 Clang 都支持在链接时使用附加链接器选项(LTCGLTO)进行跨模块内联。见Can the linker inline functions?

【讨论】:

    猜你喜欢
    • 2019-04-15
    • 2011-06-23
    • 2014-02-26
    • 2011-10-07
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多