Skip to content

Latest commit

 

History

History
97 lines (82 loc) · 2.89 KB

File metadata and controls

97 lines (82 loc) · 2.89 KB

条款44:将与参数无关的代码抽离templates

编写template时,为避免代码重复,可将相同的代码抽离包装,再放回原有的template中,这其中有个敲门。在template中,重复是隐晦的,毕竟只存在一份template源码,所以你必须训练自己去感受当template被实例化多次时可能发生的重复。

举个例子,假设你想为固定尺寸的正方矩阵编写一个template。该矩阵的性质之一时支持逆矩阵运算。

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

现在,考虑这些代码:

SquareMatrix<double, 5> sm1;
...
sm1.invert();
SquareMatrix<double, 10> sm2;
...
sm2.invert();

这会实例化两份invert,除了常量5和10,这两个函数的其他部分完全相同。

你会为它们建议一个带参数的函数,然后以5和10来调用这个函数,而不重复代码。下面是对SquareMatrix的第一次修改:

template <typename T>
class SquareMatrixBase {
protected:
  ...
  void invert(std::size_t matrixSize);
  ...
};
template <typename T, std::size_t n>
class SquareMatrixBase<T> : private SquareMatrixBase<T> { // 条款39
private: 
  using SquareMatrixBase<T>::invert();  // 条款43
public:
  ...
  void invert() { this->invert(n); }
};

SquareMatrixBase::invert 如何知道该操作什么数据?最好的办法是令SquareMatrixBase存储一个指针,指向矩阵数值所在的内存。成果看起来像这样:

template <typename T>
class SquareMatrixBase {
protected:
  SquareMatrixBase(std::size_t n, T* pMem) 
    : size(n), pData(pMem) {}
  void setDataPtr(T* ptr) { pData = ptr; }
  ...
private:
  std::size_t size;  // 矩阵大小
  T* pData;
};

这允许派生类决定内存分配方式。某些实现版本也许会决定将矩阵数据存储在SquareMatrix对象内部:

template <typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
  SquareMatrix() : SquareMatrixBase<T>(n, data) {}
  ...
private:
  T data[n * n];
};

这种类型对象不需要动态分配内存,但对象自身可能非常大。另一种做法是把每个矩阵的数据放进heap:

template <typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
  SquareMatrix() 
    : SquareMatrixBase<T>(n, 0), pData(new T[n*n])
  { this->setDataPtr(pData.get()); }
  ...
private:
  boost::scoped_array<T> pData;
};

请记住

  • template生成多个class和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
  • 因非类型模板参数而造成的代码膨胀,往往可以消除。做法是以函数参数或class成员变量替换template参数。
  • 因类型参数而造成的代码碰撞,往往可以降低,做法是让带有完全相同二进制表述的具体类型实现共享代码。