二元运算符返回 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?的主要内容,如果未能解决你的问题,请参考以下文章

STL学习思想

专业词汇-数学-运算:二元运算

** 是新的幂运算符,而不是 php 中的 power()

C++之重载运算与类型转换

instanceof是Java的一个二元操作符(运算符)

NSError 代码检查:二元运算符“==”不能应用于两个 Int 操作数