如何在rvalues的算术表达式中实现"最优"运算符重载解析?

Awa*_*aki 5 mingw operator-overloading rvalue-reference move-semantics c++11

首先,我为过于冗长的问题道歉.我想不出任何其他方法来准确地总结我的问题......现在谈到实际的问题:

我目前正在尝试使用C++ 0x rvalue引用...以下代码会产生不需要的行为:

#include <iostream>
#include <utility>

struct Vector4
{
    float x, y, z, w;

    inline Vector4 operator + (const Vector4& other) const
    {
        Vector4 r;
        std::cout << "constructing new temporary to store result"
                  << std::endl;
        r.x = x + other.x;
        r.y = y + other.y;
        r.z = z + other.z;
        r.w = w + other.w;
        return r;
    }
    Vector4&& operator + (Vector4&& other) const
    {
        std::cout << "reusing temporary 2nd operand to store result"
                  << std::endl;
        other.x += x;
        other.y += y;
        other.z += z;
        other.w += w;
        return std::move(other);
    }
    friend inline Vector4&& operator + (Vector4&& v1, const Vector4& v2)
    {
        std::cout << "reusing temporary 1st operand to store result"
                  << std::endl;
        v1.x += v2.x;
        v1.y += v2.y;
        v1.z += v2.z;
        v1.w += v2.w;
        return std::move(v1);
    }
};

int main (void)
{
    Vector4 r,
            v1 = {1.0f, 1.0f, 1.0f, 1.0f},
            v2 = {2.0f, 2.0f, 2.0f, 2.0f},
            v3 = {3.0f, 3.0f, 3.0f, 3.0f},
            v4 = {4.0f, 4.0f, 4.0f, 4.0f},
            v5 = {5.0f, 5.0f, 5.0f, 5.0f};

    ///////////////////////////
    // RELEVANT LINE HERE!!! //
    ///////////////////////////
    r = v1 + v2 + (v3 + v4) + v5;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果输出

构造新临时存储结果
构建新临时存储结果
重用临时第一操作数存储结果
重用临时第一操作数存储结果

虽然我曾希望有类似的东西

构造新临时存储结果
重用临时第一操作数来存储结果
重用临时第二操作数来存储结果
重用临时第二操作数来存储结果

在尝试重新编写编译器正在执行的操作之后(我正在使用带有选项-std = c ++ 0x的MinGW G ++ 4.5.2以防万一),它实际上看起来很合乎逻辑.该标准说,相同优先级的算术运算从左到右进行评估/分组(为什么我假设从右到左我不知道,我想这对我来说更直观).那么这里发生的事情是编译器首先评估子表达式(v3 + v4)(因为它在括号中?),然后开始将表达式中的操作从左到右与运算符重载进行匹配,从而调用Vector4 operator + (const Vector4& other)子表达式表达v1 + v2.如果我想避免不必要的临时性,我必须确保在任何带括号的子表达式的左边出现不超过一个左值操作数,这对任何使用这个"库"且无辜地期待的人来说都是违反直觉的.最佳性能(如最小化临时性的创建).

(我知道,有歧义在我的代码就operator + (Vector4&& v1, const Vector4& v2)operator + (Vector4&& other)(v3 + v4)被加入的结果v1 + v2,造成了警告,但它在我的情况下无害的,我不希望添加另一个重载两个右值引用操作数 - 有人知道是否有办法在gcc中禁用此警告?)

长话短说,我的问题归结为:是否有任何方式或模式(最好是编译器无关的)这个载体类可以改写,使表达式的括号任意使用仍然导致运算符重载的"最优"的选择(最佳在"性能"方面,即最大化与右值参考的绑定)?也许我要求太多而且这是不可能的......如果是这样的话,那也没关系.我只是想确保我没有遗漏任何东西.

提前谢谢了

附录

首先感谢我得到的快速回复,几分钟之内(!) - 我真的应该早点开始在这里发帖...

它在评论中变得乏味回复,所以我认为澄清我对这个类设计的意图是有序的.也许你可以指出我的思维过程中存在一个基本的概念缺陷,如果有的话.

你可能会注意到我没有在堆内存中保存任何资源.它的成员甚至只是标量类型.乍一看,这使得它成为基于移动语义的优化的可疑候选者(另见这个问题实际上帮助我很好地掌握了右值引用背后的概念).

但是,由于这个应该是原型的类将用于性能关键的上下文(精确的3D引擎),我想优化每一件小事.低复杂度算法和数学相关技术(如查找表)当然应该构成大部分优化,因为其他任何东西都只是解决症状而不是根除性能不佳的真正原因.我很清楚这一点.

有了这样的方式,我在这里的目的是为了与向量和矩阵本质上属于代数表达式中的旧式数据结构优化,而不指针在其中的数据(主要是由于您的数据得到堆上的性能缺点[有取消引用其他指针,缓存注意事项等.]).

我不关心移动分配或构造,我只是不希望在评估复杂的代数表达式期间创建比绝对必要的更多临时值(通常只有一个或两个,例如矩阵和向量).

那些是我的想法可能是错误的.如果是,请纠正我:

  1. 要在不依赖RVO的情况下实现这一点,必须按引用返回(再次:请记住,我没有远程资源,只有标量数据成员).
  2. 通过引用返回使得函数调用表达式成为左值,暗示返回的对象不是临时的,这是坏的,但是通过rvalue引用返回使得函数调用表达式成为xvalue(参见3.10.1),这在我的方法的背景(见4)
  3. 通过引用返回是危险的,因为对象的寿命可能很短,但是:
  4. 临时保证直到他们创建的表达式的评估结束为止,因此:
  5. 如果此rvalue引用参数引用的对象是通过引用返回的对象,那么通过引用从至少有一个rvalue-reference作为其参数的运算符返回是安全的.因此:
  6. 任何只使用二元运算符的任意表达式都可以通过在不超过一个类似PoD的类型中仅创建一个临时表来计算,并且二进制运算本质上不需要临时(如矩阵乘法)

(通过rvalue -reference 返回的另一个原因是因为它的行为类似于函数调用表达式的rvalue-ness值;并且运算符/函数调用表达式需要为rvalue才能绑定到后续调用采用rvalue引用的运算符.如(2)所述,对通过引用返回的函数的调用是lvalues,因此会绑定到具有签名的运算符T operator+(const T&, const T&),从而导致创建不必要的临时值)

我可以通过使用C风格的功能来达到理想的性能add(Vector4 *result, Vector4 *v1, Vector4 *v2),但是来吧,我们生活在21世纪......

总之,我的目标是创建一个矢量类,它使用重载运算符实现与C方法相同的性能.如果这本身是不可能的,那么我认为它无法帮助.但是,如果有人可以向我解释为什么我的方法注定要失败,我会很感激(从左到右的操作员评估问题,这当然是我的帖子的最初原因).
事实上,我一直在使用"真正的"矢量类这一个是一段时间的简化,没有任何崩溃或损坏的内存到目前为止.事实上,我从来没有真正将本地对象作为引用返回,所以不应该有任何问题.我敢说我正在做的是符合标准的.

对原始问题的任何帮助当然也会受到赞赏!

非常感谢所有人的耐心

Pup*_*ppy 1

您不应该返回右值引用,而应该返回一个值。此外,您不应同时指定成员自由运算符+。我很惊讶甚至编译了。

编辑:

r = v1 + v2 + (v3 + v4) + v5;
Run Code Online (Sandbox Code Playgroud)

当您执行两项子计算时,怎么可能只有一个临时值?那是不可能的。您无法重写标准并更改它。

您只需要相信您的用户会做一些不完全愚蠢的事情,例如编写上面的代码行,并期望只有一个临时代码。