c++ - 从模板中提取与参数无关的代码

标签 c++ templates inline

此问题涉及 Effective C++ 一书中的第 44 项。 Scott Mayers 指出以下模板类可能会导致代码膨胀,因为 invert 函数不一定依赖于模板参数 n。不幸的是,具有不同 n 值的多个模板实例,例如 SquareMatrix 和 SquareMatrix 也会生成反转函数的多个实例,从而生成比实际应该更大的目标代码。

template<typename T, std::size_t n>
class SquareMatrix
{   
public:
   void invert()
   {
      ...
   }
};

他建议可以在基类中分解出反转函数,如下所示。请注意,volatile var 仅用于测试目的,以防止编译器优化所有内容。 SquareMatrixBase::invert 不应该做任何合理的事情。我只是想检查它的代码是否重复。

template<typename T>
class SquareMatrixBase
{
protected:
   void invert(std::size_t size)
   {
      volatile int var = size;
   }
};

template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T>
{
private:
   using SquareMatrixBase<T>::invert;

public:
   void invert()
   {
      invert(n);
   }
};

此时 Scott Mayers 说:

now many -- maybe all -- of SquareMatrix's member functions can be simple inline calls to non-inline base class versions that are shared with all other matrices holding the same type of data, regardless of their size.

但是,我不明白为什么编译器不应该也内联 SquareMatrixBase::invert,这会导致代码膨胀。为什么 Scott Mayers 谈论“调用非内联基类版本”?就我现在而言,模板类的成员函数始终隐式符合内联条件,除非我通过某些特定指令强制编译器不这样做。

作为测试,我使用 gcc 和 O3 优化级别编译了以下主要函数

int main()
{
   {
      SquareMatrix<int, 5> sm;
      sm.invert();
   }
   {
      SquareMatrix<int, 10> sm;
      sm.invert();
   }       
   return 0;
}

并且生成的目标代码清楚地表明 BaseSquareMatrixBase::invert 是内联的,因此导致了重复的目标代码。

0000000000400400 <main>:
class SquareMatrixBase
{
protected:
   void invert(std::size_t size)
   {
      volatile int var = size;
  400400:       c7 44 24 f8 05 00 00    movl   $0x5,-0x8(%rsp)
  400407:       00 
   {
      SquareMatrix<int, 10> sm;
      sm.invert();
   }       
   return 0;
}
  400408:       31 c0                   xor    %eax,%eax
class SquareMatrixBase
{
protected:
   void invert(std::size_t size)
   {
      volatile int var = size;
  40040a:       c7 44 24 fc 0a 00 00    movl   $0xa,-0x4(%rsp)
  400411:       00 
   {
      SquareMatrix<int, 10> sm;
      sm.invert();
   }       
   return 0;
}
  400412:       c3                      retq 

我错过了什么?

最佳答案

在这个具体实例中,您和编译器都希望内联对 SquareMatrixBase<int>::invert() 的调用,因为它太小了。用更大的invert()函数,编译器将在内联或调用之间做出权衡——看看 gcc 使用 -Os 做了什么很有趣,例如,使用完全实现的 invert() — 但如果基类不是模板化类(或者如果您只计划支持有限数量的实例化),您可以选择通过在不同的编译单元中提供实现来强制解决这个问题。

关于c++ - 从模板中提取与参数无关的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28914103/

相关文章:

c++ - 使用 TransparentBlt

c++ - std::array 作为虚函数中的参数

C++11 - 模板 std::enable_if 和 std::result_of

c++ - 将类实例作为指针传递并在此指针上使用重载的下标运算符

c++ - 使用模板将类方法绑定(bind)到特定原型(prototype)

c++ - 获取重载函数模板的地址

css - 使用显示 :inline-block 渲染行与行之间有白色间隙的多行文本组件

f# - 当嵌套是可选的时,嵌套 f# 函数与不嵌套它们有何不同?

css - 使用 React 内联自定义 `::-webkit-scrollbar`

c++ - dynamic_cast 抛出的 std::bad_cast 异常