数值向量运算符重载+右值参考参数

Lar*_*Chr 9 c++ numerical operator-overloading rvalue-reference

我有下面的数字向量模板类(用于数值计算的向量).我正在努力编写D=A+B+C所有变量都是Vector对象的地方.A,B并且C不应该修改.我的想法是使用,Vector operator+(Vector&& B)以便在(希望)Vector返回Rvalue之后B+C,所有后续添加都存储在该对象中,即为所有后续添加窃取Rvalue的存储.这是为了消除新对象的创建和所需的存储.

我的问题是,我可以从调用的每个函数的输出语句中看到,Vector operator+(Vector&& B)从未调用过.我不明白为什么,因为,如果我有一个重载虚拟函数foo(Vector& B)foo(Vector&& B)和尝试foo(A+B+C),那么第二个功能是完全相同称为我所希望的.

很抱歉这个冗长的问题,但这是我在这里的第一个问题,我想尽量保持清醒.

关于我明显做错了什么或为什么我不应该尝试这个的任何建议,将不胜感激.

template <typename T>
class Vector
{
        int n;
        T* v;
        Vector();
        ~Vector();
        Vector(const Vector& B);
        Vector(Vector&& B);
        inline Vector operator+(const Vector& B) const;
        inline Vector operator+(Vector&& B) const;
};

template <typename T>
Vector<T>::Vector(const Vector<T>& B)
{
        ...
}

template <typename T>
Vector<T>::Vector(Vector<T>&& B)
{
        ...
}

template <typename T>
Vector<T> Vector<T>::operator+(const Vector<T>& B) const
{
        Vector<T> C;
        ...
        return C;
}

template <typename T>
Vector<T> Vector<T>::operator+(Vector<T>&& B) const
{
        ...do stuff to B
        return B;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 7

在表达式中:

D=A+B+C
Run Code Online (Sandbox Code Playgroud)

A并且B是左值,所以呼叫A+B呼叫Vector::operator(const Vector&)

返回一个rvalue,让我们调用它tmp,所以下一个子表达式是tmp+C.

C也是一个左值,所以Vector::operator(const Vector&)再次调用.返回另一个右值,让我们调用它tmp2

最后一个子表达式是D=tmp2,但您的类型没有移动赋值运算符,因此使用隐式定义的复制赋值运算符.

即你永远不会operator+在右侧使用右值调用,并且唯一具有rvalue参数的表达式是你没有为rvalues定义的赋值.

定义重载的非成员运算符会更好:

Vector operator+(const Vector&, const Vector&);
Vector operator+(Vector&&, const Vector&);
Vector operator+(const Vector&, Vector&&);
Vector operator+(Vector&&, Vector&&);
Run Code Online (Sandbox Code Playgroud)

这适用于rvalues和lvalues的任意组合.(一般来说operator+,通常应该是非会员.)

编辑:下面的替代建议不起作用,在某些情况下会导致含糊不清.

另一个替代方案,如果你的编译器支持它(我认为只有clang),那就是保留你现有的Vector::operator+(Vector&&)但是用Vector::operator+(const Vector&)一个ref-qualifier来区分你的两个重载:

Vector Vector::operator+(const Vector& v) const&
{
  Vector tmp(*this);
  tmp += v;
  return tmp;
}

Vector Vector::operator+(const Vector& v)&&
{
  *this += v;
  return std::move(*this);
}
Run Code Online (Sandbox Code Playgroud)

*this当它已知为rvalue时重新使用,即当加法的左侧是rvalue时它使用移动语义,与原始代码相比,原始代码只能在右侧是rvalue时使用移动语义.(注意上面的代码假设你已按照operator+=David Rodriguez的回答中的建议定义了一个成员)

  • @larsc:你不需要(也不想)自由函数版本的第一个参数的rvalue-reference.您应该通过值*获取第一个参数*,并让编译器处理它.签名中*rvalue-reference*的问题是(并且因为它是模板)它也会绑定到*lvalue*然后你可能会在内部做错事(当然,除非你提供两个重载). (2认同)
  • 我不同意不需要右值引用,我的解决方案显示了四个重载,它们将处理左值和右值的任意组合,并在可能的情况下使用避免副本。按值获取参数的单个函数无法做到这一点。 (2认同)