在执行添加时如何使用 c++ 模板避免临时对象
How temporary objects get avoided by using c++ templates when doing additions?
我正在阅读Tomas Arce的一篇文章。本文讨论了如何使用模板提高矢量添加性能。但有些部分我跟不上。
笔者说下面的代码可以避免在评估时生成临时对象 vA = vB + vC + vD
,但是怎么做呢?我不明白如何避免临时对象。有没有人可以解释使用模板时如何避免临时对象的内部机制。
template< class ta_a >
class vecarg
{
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A ) {}
inline const float Evaluate( const int i ) const
{ return Argv.Evaluate( i ); }
};
template<>
class vecarg< const float >
{
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A ) {}
inline const float Evaluate( const int i ) const { return Argv; }
};
template<>
class vecarg< const int >
{
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A ) {}
inline const float Evaluate( const int i ) const { return (float)Argv; }
};
template< class ta_a, class ta_b, class ta_eval >
class vecexp_2
{
const vecarg<ta_a> Arg1;
const vecarg<ta_b> Arg2;
public:
inline vecexp_2( const ta_a& A1, const ta_b& A2 )
: Arg1( A1 ), Arg2( A2 ) {}
inline const float Evaluate ( const int I ) const
{ return ta_eval::Evaluate( i, Arg1, Arg2 ); }
};
// Listing 5
附言在@Severin Pappadeux提供的第二个链接中,解释很容易理解。基本上,所有表达式模板所做的都是:通过重载运算符+
它甚至不执行加法,而是创建一个轻量级对象,其主要工作是在运算符+
的每一侧保存对两个操作数的两个引用,并且加法是在计算运算符=
时完成的。通过第二次重载运算符+
(因为左侧是轻量级对象,右侧是vD
(,可以创建对轻量级对象的引用(其中包含之前对 vB
和 vC
的两个引用(和vD
,并且在计算运算符时再次完成添加=
。
把这篇文章拿起底特律大小的盐。它已经有 14 年的历史了,从那时起,优化器有了显着改进。简而言之,这家伙正在使用模板来制作一个复杂的方法网络,这些方法将A = B + C + D从:
// Taking liberties with pseudocode and notation for clarity. This is
// a very rough oversimplification
// Create a new, wasteful, vector to store a temporary result
vector tmp(C.x + D.x, C.y + D.y, C.z + D.z)
// Create another new, wasteful, vector to store a temporary result:
vector tmp2(B.x + tmp.x, B.y + tmp.y, B.z + tmp.z)
// Waste even more time copying the result.
A.x = tmp2.x; A.y = tmp2.y; A.z = tmp2.z;
// In reality, your optimized compiler isn't even remotely this stupid.
他正在使用模板预处理器来消除临时和最终副本的创建。他试图达到执行这样的事情的地步:
A.x = B.x + (C.x + D.x);
A.y = B.y + (C.y + D.y);
A.z = B.z + (C.z + D.z);
记住,如果你把这样的东西放在其他工程师必须阅读的生产代码中,你最好有一个很好的理由。例如,您必须证明使用此胡言乱语可以提高性能。为什么?因为维持这种精神错乱的成本是不可忽视的。在使用类似内容之前,请获取性能数字。
技术被称为"表达式模板",它的发明者托德·维尔德惠森(Todd Veldhuizen(有几篇论文,从
http://ubietylab.net/ubigraph/content/Papers/pdf/ExpressionTemplates.pdf
和
http://www.drdobbs.com/cpp/expression-templates/184401656
最后是托德报告
http://www.cs.indiana.edu/pub/techreports/TR542.pdf
整个概念依赖于存储对本机类型的引用,或计算折叠到本机类型的表达式。
在此过程中有临时对象参与,但它们存储的只是对源数据和目标数据的引用,这些数据在某个时候被解析。在没有优化的情况下进行编译确实可以分配空间,但如果启用了优化,优化程序会注意到没有理由这样做。
也就是说,如果这是为使用寄存器的 CPU 编译的。基于堆栈的机器的行为可能有所不同,因为操作仍然需要推送和弹出其操作数。
- 在不复制临时对象的情况下延长其生存期
- 为什么当我们有常量引用时创建临时对象?
- 程序如何'remember'临时对象?
- 返回对临时对象的引用
- 防止临时对象文件访问 MSVC 中的磁盘
- 是否可以在C++中移动临时对象的属性?
- 通过引用传递临时对象
- 临时C++对象是否为左值?
- 临时对象:术语澄清
- 存储对(可能)临时对象的引用是否合法,只要引用不比对象存活?
- 临时对象有身份吗?
- 临时对象上的运算符重载
- 如何在没有 std::move 的情况下移动临时对象
- 临时对象在C++中是不可避免的吗?
- 编译错误:临时对象构造函数中缺少参数
- 为什么在按值返回时创建临时对象,而不是在按值传递给函数参数时创建临时对象
- 我试图创建临时对象的方式有错误吗
- 子表达式中临时对象的生存期
- 对临时对象的Const引用不会延长其生存期
- 为什么引用类型在使用临时对象访问时是左值