草庐IT

c++ - 二元运算符返回 Xvalue 而不是 PRvalue?

coder 2024-02-12 原文

根据 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 来展示这两种实现方式.

如果这样做是错误的/不好的,谁能解释一下,为什么?我想不出为什么不这样做的原因。它节省了许多构造函数和析构函数调用,并且似乎没有破坏任何东西。

最佳答案

您在这里使用移动构造函数对您的代码进行了修改。矩阵加法可以安全地完成,根本不需要移动构造函数,而且编译器足够聪明,可以优化它。

下面是一些测试代码来证明我在说什么:

#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

根本没有任何复制/移动构造函数或任何函数调用的踪迹。一切都展开成快速的数学研磨指令流。

说真的,没有必要为右值编写额外的处理程序。没有它们,编译器会生成完美的代码。

关于c++ - 二元运算符返回 Xvalue 而不是 PRvalue?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22237275/

有关c++ - 二元运算符返回 Xvalue 而不是 PRvalue?的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  6. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  7. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

  8. ruby-on-rails - 只有当不是 nil 时才执行映射? - 2

    如果names为nil,则以下中断。我怎样才能让这个map只有在它不是nil时才执行?self.topics=names.split(",").mapdo|n|Topic.where(name:n.strip).first_or_create!end 最佳答案 其他几个选项:选项1(在其上执行map时检查split的结果):names_list=names.try(:split,",")self.topics=names_list.mapdo|n|Topic.where(name:n.strip).first_or_create!e

  9. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  10. ruby - 定义自定义 Ruby 运算符 - 2

    问题是:除了在“OperatorExpressions”?例如:1%!2 最佳答案 是的,可以创建自定义运算符,但有一些注意事项。Ruby本身并不直接支持它,但是superatorsgem做了一个巧妙的把戏,将运算符链接在一起。这允许您创建自己的运算符,但有一些限制:$geminstallsuperators19然后:require'superators19'classArraysuperator"%~"do|operand|"#{self}percent-tilde#{operand}"endendputs[1]%~[2]#Out

随机推荐