二元运算符返回 Xvalue 而不是 PRvalue?
Posted
技术标签:
【中文标题】二元运算符返回 Xvalue 而不是 PRvalue?【英文标题】:Binary Operators Return Xvalue Instead of PRvalue? 【发布时间】:2014-03-06 22:15:38 【问题描述】:根据this 博客——我意识到它已经过时了,如果它不再被认为是相关的,请告诉我——实现二元运算符的最佳方法如下......
// The "usual implementation"
Matrix operator+(Matrix const& x, Matrix const& y)
Matrix temp = x; temp += y; return temp;
// --- Handle rvalues ---
Matrix operator+(Matrix&& temp, const Matrix& y)
temp += y; return std::move(temp);
Matrix operator+(const Matrix& x, Matrix&& temp)
temp += x; return std::move(temp);
Matrix operator+(Matrix&& temp, Matrix&& y)
temp += y; return std::move(temp);
我测试了这个实现,并在如下表达式中......
a + b + c + d
它们都是矩阵,我最终得到了许多我认为没有必要的移动构造函数和析构函数调用。如果所有带有右值矩阵的 operator+ 的返回类型都更改为 Matrix&&,则您消除了所有移动构造函数,并且只需要一个析构函数调用。
我用代码here做了一个简单的程序来展示这两种实现。
谁能解释这样做是否错误/不好,为什么?我想不出为什么不这样做的理由。它节省了许多构造函数和析构函数调用,而且似乎没有破坏任何东西。
【问题讨论】:
operator+(Matrix const& x, Matrix const& y)
会返回什么?我希望不是对temp
的引用。另外,如果有人写Matrix&& x = a + b + c; DoSomething(x);
他们会很糟糕; x
在初始化后不久就变成了一个悬空引用。
这有助于***.com/questions/11726171/… ?
@IgorTandetnik 可以从右值引用初始化左值引用吗?
@Jay:我在发布后不久就修复了它。你打败了我。诚然,这是一个相当牵强的场景。
operator+(Matrix const& x, Matrix const& y) 仍然返回左值,如上所示。在这种情况下,您最终会调用一个复制构造函数和一个移动构造函数调用。编辑:你改变了它。这似乎有点令人担忧,但我想不出创建矩阵 && 并保存它的理由。
【参考方案1】:
您在这里使用移动构造函数来破坏您的代码。矩阵加法可以安全地完成,根本不需要移动构造函数,编译器足够聪明,可以优化掉它。
这里有一些测试代码来证明我在说什么:
#include <stdint.h>
class Matrix3
public:
float Mtx[3][3];
inline Matrix3() ;
inline Matrix3 operator+( const Matrix3& Matrix ) const
Matrix3 Result;
for ( size_t i = 0; i != 3; ++i )
for ( size_t j = 0; j != 3; ++j )
Result.Mtx[i][j] = Mtx[i][j] + Matrix.Mtx[i][j];
return Result;
virtual int GetResult() const
int Result = 0;
for ( size_t i = 0; i != 3; ++i )
for ( size_t j = 0; j != 3; ++j )
Result += (int)Mtx[i][j];
return Result;
;
int main()
Matrix3 M;
Matrix3 M1;
Matrix3 M2;
Matrix3 M3;
Matrix3 M4;
M = M1 + M2 + M3 + M4;
return M.GetResult();
我使用GCC: (GNU) 4.9.0 20131110 (experimental)
如下:g++ -O3 main.cpp -S
输出程序集如下所示:
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $176, %esp
call ___main
fnstcw 14(%esp)
fldz
fadd %st(0), %st
fadds LC0
fadds LC0
fsts 140(%esp)
movl 140(%esp), %eax
fsts 144(%esp)
movl %eax, 20(%esp)
movl 144(%esp), %eax
fsts 148(%esp)
movl %eax, 24(%esp)
fsts 152(%esp)
movl 148(%esp), %eax
fsts 156(%esp)
movl %eax, 28(%esp)
fsts 160(%esp)
movl 152(%esp), %eax
fsts 164(%esp)
movl %eax, 32(%esp)
fsts 168(%esp)
movl 156(%esp), %eax
fstps 172(%esp)
movl %eax, 36(%esp)
movl 160(%esp), %eax
flds 24(%esp)
movl %eax, 40(%esp)
movl 164(%esp), %eax
movl %eax, 44(%esp)
movl 168(%esp), %eax
movl %eax, 48(%esp)
movl 172(%esp), %eax
movl %eax, 52(%esp)
movzwl 14(%esp), %eax
movb $12, %ah
movw %ax, 12(%esp)
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %edx
flds 20(%esp)
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 28(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 32(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 36(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 40(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 44(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 48(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
flds 52(%esp)
addl %eax, %edx
fldcw 12(%esp)
fistpl 8(%esp)
fldcw 14(%esp)
movl 8(%esp), %eax
leave
addl %edx, %eax
ret
根本没有任何复制/移动构造函数或任何函数调用的痕迹。一切都展开成快速的数学运算指令流。
说真的,没有必要为 r 值编写额外的处理程序。编译器可以在没有它们的情况下生成完美的代码。
【讨论】:
如果默认构造函数是微不足道的,这是正确的,但是如果,例如,它将矩阵归零,那么这比我建议的要慢。显然,所有这些选项都取决于您正在使用的类(Matrix 只是链接网站中的一个示例),但我相信如果您按照我发布的路线返回 XValues 而不是 PRValues 总是更快。以上是关于二元运算符返回 Xvalue 而不是 PRvalue?的主要内容,如果未能解决你的问题,请参考以下文章