我正在阅读一本C ++书籍,我正在讨论减少模板生成的目标代码( Effective C ++ III作者:Scott Meyers )。它给出的一个例子是:
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()); }
... functions ...
private:
boost::scoped_array<T> pData;
};
其中基类SquareMatrixBase有一个名为:
的函数void invert(std::size_t matrixSize);
“这本书继续说”无论数据存储在何处,都可以 从膨胀的角度来看,关键结果是现在很多 - 也许全部 - SquareMatrix的成员函数可以是简单的内联调用 与所有其他矩阵共享的非内联基类版本 持有相同类型的数据,无论其大小如何。“
“内联调用非内联基类版本......”是什么意思?如果它是内联调用我会认为它会将任何函数的整个基类版本放入使用内联的地方,但这会导致我想到的相同代码膨胀。它说这就像它对代码膨胀的好处。
如果您需要更多背景信息让我知道,这些章节很长,但我很努力地提供背景信息,但我可能错过了一些东西。
在这段经文中使用方矩阵和方阵基的目的是:
SquareMatrix最初是一个独立模板(未派生)。它包含一系列基于模板参数n值进行操作的函数。因此,对于每个使用的n值(或者对于每对n,使用的T),每个函数基本上都有一个副本作为新模板,并为每个参数对实例化这些函数。创建SquareMatrixBase是为了将依赖于size参数的函数移动到基类。由于基类仅使用类型参数(而不是大小)进行实例化,因此可以通过传入派生类传递给基类构造函数的size值来调用基类中的函数。这意味着传入SquareMatrix模板的每个typename只有一个版本的函数,无论传入的是std :: size_t n(而不是每个{T,n}组合的每个函数的一个版本。
答案 0 :(得分:6)
关键是SquareMatrix::invert()
是内联的,因此函数甚至不会出现在结果代码中,而是直接调用受保护的基函数SquareMatrixBase::invert(n)
。
既然该函数不内联,那么该函数只有一个实例(对于每种类型T
),而不是每个大小n
的一个副本。这与单类设计形成鲜明对比,其中一个invert()
函数将为n
的每个值实例化。
答案 1 :(得分:3)
这意味着SquareMatrix
类中的(许多)函数将是简单的内联函数;这些内联函数将包含对实现该功能的基类函数的非内联调用。例如,SquareMatrix
可能有一个invert()
成员,内联实现为:
void invert() {SquareMatrixBase<T>::invert(n);}
调用它应该生成与直接调用SquareMatrixBase::invert(n)
完全相同的代码,但是方便的是n
的值由模板作为编译时常量提供,同时保存了两者调用代码和实现必须将其作为运行时变量进行跟踪。
答案 2 :(得分:2)
这意味着派生类将包含内联函数,它将调用基类中的(重)非内联函数进行实际工作。比较的两个变体是:
template <typename T, std::size_t n>
class SquareMatrix {
...
void invert();
...
};
template <typename T, std::size_t n>
void SquareMatrix::invert() {
... heavy code ...
}
和
template <typename T, std::size_t n>
class SquareMatrix : SquareMatrixBase<T> {
...
void invert() { SquareMatrixBase<T>::invert(pData.get(), n); }
...
};
template <typename T>
void SquareMatrixBase::invert(T* data, int n){
... heavy code ...
}
现在第二个每T
发出一次重代码,而第一个代码按T
和n
发出一次重代码。这意味着在第二种情况下更多(几乎完全相同)的代码。
请注意,对此技巧使用继承不是强制性的,我个人只会使用自由函数(可能在私有命名空间中)。
编辑:内联基本上是函数体替换函数调用(beta减少,ha!)。从
my_matrix.invert()
你得到例如。
SquareMatrixBase<float>::invert(my_matrix.pData.get(), 3); <-- pseudocode
在主叫代码中。
答案 3 :(得分:0)
如果没有阅读这篇文章,就很难确切地说出这里发生了什么,但如果SquareMatrix
是模板基类SquareMatrixBase
周围的模板化“包装”类,那么SquareMatrix
基本上是将相应的模板参数传递给基类,并为基类的函数提供几个非静态数据成员来操作,然后就可以了,因为成员函数SquareMatrix
基本上是简单的函数,只调用基类中定义的函数,内联SquareMatrix
的所有成员函数。另一方面,基类SquareMatrixBase
将具有所有未加内联的“繁重”函数,而是包含在另一个已编译的代码模块中。
例如,您提到SquareMatrixBase
有一个名为:
void SquareMatrixBase<T>::invert(std::size_t matrixSize);
在SquareMatrix
中,您可以简单地将此功能称为:
template<typename T, std::size_t n>
inline void SquareMatrix<T, std::size_t n>::invert() { SquareMatrixBase<T>::invert(n); }
内联代码版本,即使可以通过许多不同类型和大小实例化,也会编译为单个函数调用...“重”基类函数只需要为每个不同的类型实例化,而不是每个不同的 size 的每个排列类型的矩阵可能需要用...实例化,这样肯定会减少代码臃肿。因此SquareMatrixBase<T>
只需要为int
,double
等类型进行实例化。它包含耗尽大量代码的所有“主要”函数。另一方面的SquareMatrix<T, std::size_t n>
不仅可以int
实例化,还可以实现int
和{1,2,3,4,...}大小。矩阵大小的每个不同模板参数将实例化SquareMatrix<T, std::size_t n>
的新版本,但仅对int
SquareMatrixBase<T>
版{{1}}使用单个实例化。