Operator with variable numerical vector variable + rvalue

I have a class of a numerical vector template below (a vector for numerical calculations). I am trying to record D=A+B+C , where all variables are Vector objects. A , B and C should not be changed. My idea is to use Vector operator+(Vector&& B) so that after (hopefully) the Rvalue Vector returned from B+C , all subsequent additions are stored in this object, i.e. Rvalue storage is stolen for all subsequent additions. This is done in order to exclude the creation of new objects and the required storage.

My problem is that I can see from the output statements from each function called Vector operator+(Vector&& B) that is never called. I cannot understand why, because if I have an overloaded dummy function foo(Vector& B) and foo(Vector&& B) and try foo(A+B+C) , then the second function is called exactly as I had hoped.

Sorry for the long question, but this is my first question, and I want to try to be as clear as possible.

Any suggestions regarding what I am obviously doing wrong or why I should not try this will be appreciated.

 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; } 
+7
source share
2 answers

In the expression:

 D=A+B+C 

A and B are lvalues, so calling A+B calls Vector::operator(const Vector&)

This returns an rvalue, let it call it tmp , so the next subexpression is tmp+C

C also an lvalue, so it calls Vector::operator(const Vector&) again. This returns a different r value, lets call it tmp2

The final subexpression is D=tmp2 , but your type does not have a redirection operator, so the implicit copy assignment operator is used.

i.e. you never call operator+ with an rvalue on the right side, and the only expression that has an rvalue argument is an assignment that you did not define for rvalues.

It would be better to define overloaded operators that are not members:

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

This will work for any combination of r and lvalues. (Normally, operator+ should usually be non-member.)

Edit: the alternative sentence below does not work, in some cases this leads to ambiguity.

Another alternative, if your compiler supports it (I think that only clang does), should have kept your existing Vector::operator+(Vector&&) , but replaced your Vector::operator+(const Vector&) with two overloads that differ in 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); } 

Reuses *this when it is known as rvalue, i.e. uses move semantics when the left side of addition is an rvalue, compared to the source code, which can only use move semantics when the right side is an rvalue. (NB in ​​the above code, it is assumed that you defined the operator+= member as suggested in David Rodriguez's answer)

+5
source

I would suggest that you provide operator+= as a member method, and then reuse this from the free operator+ function, defined as:

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

Given the call to a + b + c , which is grouped as (a+b) + c , the compiler will create a copy of a for the first call to operator+ , change this in place ( lhs += rhs ), and then move it to the return value. Then it will move this return value (if it does not move the move) to the argument of the second operator+ , where it will be changed in place again, and then move to the return value.

In general, one new object will be created containing the result a+b+c , providing semantics equivalent to:

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

But with a prettier, more compact syntax a + b + c .


Note that this will not handle a + (b+c) kindly and will create two objects, if you want to support them, you will need to create several overloads.

+1
source

All Articles