这个问题涉及Effective C ++书中的第44项。 Scott Mayers指出,以下模板类可能会导致代码膨胀,因为反转函数不一定取决于模板参数n。不幸的是,具有不同n值的多个模板实例,例如SquareMatrix< int,5>和SquareMatrix< int,10>也会生成反转函数的多个实例,从而生成比实际应该更大的目标代码。
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说:
现在很多 - 也许全部 - SquareMatrix的成员函数都可以 对共享的非内联基类版本的简单内联调用 与所有其他矩阵保持相同类型的数据,无论如何 他们的大小。
但是,我不明白为什么编译器也不应该内联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
我错过了什么?
答案 0 :(得分:1)
在这个具体实例中,您和编译器都希望内联对SquareMatrixBase<int>::invert()
的调用,因为它太小了。使用更大的invert()
函数,编译器将在内联或调用之间进行权衡 - 看看gcc对-Os的作用很有意思,例如,使用完全实现的invert()
- 但是如果基类不是模板化类(或者如果您只计划支持有限数量的实例化),则可以通过在不同的编译单元中提供实现来强制解决问题。