带内联的显式模板函数实例化

时间:2014-10-15 22:01:47

标签: c++ templates

所以一位同事和我一直在讨论显式模板实例化的好处,当涉及到减少编译时间,将声明与定义分离,而不影响我编写的用于其他项目的C ++数学库的性能时。 / p>

基本上我有一个有用的数学函数库,用于处理像Vector3,Vector4,Quaternion等原语。所有这些都是用于模板参数是float或double(在某些情况下是int)

因此我不必两次编写这些函数,一次用于浮点数一次为double,函数实现是模板化的,如下所示:

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次函数。

有没有办法通过调整方法来允许内联?

我发现很难只是谷歌搜索这些问题的答案,标准规范很难理解这些主题(至少对我来说)。

BTW,预计将使用VS2010,VS2012和GCC 4.7进行编译。

任何帮助将不胜感激。

谢谢

1 个答案:

答案 0 :(得分:3)

我假设您的技巧与此问题的答案相同: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都支持在链接时使用附加链接器选项( LTCG LTO )进行交叉模块内联。见Can the linker inline functions?