1.模板类生成多个类和多个函数,所以任何模板类代码都不该与某个造成膨胀的模板参数产生相依关系。例如一个正方矩阵具有一个矩阵求逆的成员函数invert():
template<typename T, std::size_t n> //该模板类支持n*n矩阵,元素类型为T,std::size_t属于非类型参数
class SquareMatrix{
public:
void invert(); //矩阵求逆运算
...
};
如果这个模板类在具现化时接受相同元素类型(如double)而大小不同的参数时,便可能生成两份invert()函数,造成代码膨胀。
PS:正如作者后面所说,代码膨胀的版本有可能在性能上更优,因此实际选择上可能无法避免。书中很多建议都是这样——照着做了会有好处,但是也会丧失一些其他优点——这需要在实际应用中权衡选择。
2.对于非类型参数(如上面例子中的std::size_t)引起的代码膨胀,往往可以消除,做法是以函数参数或类成员变量替换模板参数。
例如上面的例子中,可以在基类中实现invert()函数,但只提供包含矩阵大小的参数,这样真正的矩阵类就可以以派生类的方式调用该函数,多个相同元素类型而不同尺寸的矩阵对象就可以调用一份带有参数的invert()函数:
template<typename T> //与尺寸无关的基类
class SquareMatrixBase{
protected:
SquareMatrixBase(std::size_t n, T* pMem) : size(n), pData(pMem){}
void setDataPtr(T* ptr) { pData = ptr;} //存储矩阵大小和一个指针指向矩阵内容
void invert(std::size_t matrixSize); //以给定尺寸求逆
...
private:
std::size_t size; //矩阵大小
T* pData; //指针,指向矩阵的内容,否则invert()不知道要操作什么类型的数据。有些实现版本也可以在类内部直接分配内存来初始化矩阵内容,也可以通过动态内存分配存储内容
};
template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T>{ //只是调用基类中的实现,而非为了表现出“is-a“关系,故使用私有继承
private:
using SquareMatrixBase<T>::invert; //避免同名函数的名称遮掩
...
public:
void invert(){ this->invert(n);} //内联函数,调用基类版本的invert(),同样this指针也是为了防止同名函数的遮掩
...
};
3.因类型参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具现类型共享实现码。
比如许多平台上int和long有相同的二进制表述,连接器可以实现代码合并;而大多数平台上所有的指针类型都有相同的二进制表述,因此凡模板类或模板函数包含指针的,往往应该对每一个成员函数使用唯一一份底层实现(调用无类型指针完成相同的操作)。