9

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

我的问题是我可以从每个Vector operator+(Vector&& B)从未调用过的函数的输出语句中看到。我不明白为什么,因为如果我有一个重载的虚拟函数foo(Vector& B)foo(Vector&& B)try 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;
}
4

2 回答 2

7

在表达式中:

D=A+B+C

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

它返回一个右值,我们称之为tmp,所以下一个子表达式是tmp+C

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

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

即,您永远不会operator+在右侧使用右值调用,并且唯一具有右值参数的表达式是您尚未为右值定义的赋值。

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

Vector operator+(const Vector&, const Vector&);
Vector operator+(Vector&&, const Vector&);
Vector operator+(const Vector&, Vector&&);
Vector operator+(Vector&&, Vector&&);

这适用于右值和左值的任何组合。(总的来说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);
}

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

于 2012-07-30T17:10:35.577 回答
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创建一个副本,在原地 ( ) 修改该副本,然后将其移动到返回值中。然后它将返回的值(除非它忽略移动)移动到 second 的参数中,在那里它将再次被修改,然后移动到返回值。aoperator+lhs += rhsoperator+

总体而言,将创建一个新对象,保存 的结果a+b+c,提供等效于的语义:

Vector<T> tmp = a;
tmp += b;
tmp += c;

但有了更好、更紧凑的语法a + b + c


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

于 2012-07-30T17:22:21.987 回答