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

时间:2012-07-30 17:02:57

标签: c++ operator-overloading numerical rvalue-reference

我有下面的数字向量模板类(用于数值计算的向量)。我正在尝试编写D=A+B+C,其中所有变量都是Vector个对象。不应修改ABC。我的想法是使用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;
}

2 个答案:

答案 0 :(得分:7)

在表达式中:

D=A+B+C

AB是左值,因此致电A+B来电Vector::operator(const Vector&)

返回右值,我们称之为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&&);

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

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

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

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);
}

当已知它是一个右值时,它重用*this,即当加法的左侧是右值时,它使用移动语义,与原始代码相比,原始代码只能在右侧使用移动语义 - 手边是右值。 (N.B.上面的代码假设您已根据David Rodriguez的回答中的建议定义了成员operator+=

答案 1 :(得分:1)

我建议你提供operator+=作为成员方法,然后从定义为的自由函数operator+中重用它:

template <typename T>
Vector<T> operator+( Vector<T> lhs,              // by value
                     Vector<T> const & rhs ) {
    lhs += rhs;
    return lhs;
}

如果调用a + b + c,其分组为(a+b) + c,编译器将为a的第一次调用创建operator+的副本,并对其进行修改({ {1}})然后将其移动到返回的值中。然后它将移动返回的值(除非它忽略移动)进入第二个lhs += rhs的参数,在那里它将再次被修改,然后移动返回值。

总的来说,将创建一个新对象,保存operator+的结果,提供相当于的语义:

a+b+c

但是使用更好,更紧凑的语法Vector<T> tmp = a; tmp += b; tmp += c;


请注意,这不会优雅地处理a + b + c,并且会创建两个对象,如果您想支持它,则需要产生多个重载。