1. Templates 生成多个 classes 和多个函数,所以任何 template 代码都不该与某个造成膨胀的 template 参数产生相依关系。
2. 因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数或 class 成员变量替换 template 参数。
3. 因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representations)的具现类型(instantiation types))共享实现码。
问题描述
如果 templates 使用不当可能会导致代码膨胀:其二进制码带着重复(或几乎重复)的代码、数据。
在 non-template 代码中,重复的代码清晰可见:你可以看到两个函数或两个类直接所有重复的代码。然而在 template 代码中,重复是隐晦的:毕竟只有一份 template 源码,所以必须自己去感受当 template 被具化多次时可能发生的重复。
例如:
// n x n矩阵
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,这两个函数可能只有与参数有关的数据会不同,其他的部分完全相同。这就是 template 引出代码膨胀的一个典型例子。
解决方式
如果你看到两个函数完全相同,只除了一个使用了5而一个使用了10,你本能地会想创建一个函数,将5和10作为参数传进去,而不使用重复的代码。因此对上面的 SquareMatrix 进行第一次修改:
template<typename T>
class SquareMatrixBase { // 与尺寸无关的base class
protected:
...
void invert(std::size_t matrixSize); // 以给定的尺寸求逆矩阵
...
};
template<typename T, std::size_t>
class SquareMatrix: private SquareMatrixBase<T> {
private:
// 避免遮掩base版的invert,见条款33
using SquareMatrixBase<T>::invert;
public:
...
void invert() { this->invert(n); } // 调用base class的invert
};
上述代码中,带参数的 invert 位于 base class SquareMatrixBase 中。SquareMatrixBase 也是一个 template,但它只对“矩阵元素对象的类型”参数化,不对矩阵的尺寸参数化。因此对于某给定的元素对象类型,所以矩阵共享一个 SquareMatrixBase class。它们也将因此共享这个唯一的 class 内的 invert。
调用基类的 invert 时,如果不使用“this->”记号,模板化基类(如 SquareMatrixBase< T>)内的函数名称会被派生类掩盖(见条款43)。也请注意,上述代码的继承关系时 private,这说明此处的 base class 只是为了帮助 derived class 实现,而不是 is-a 关系。
评论区