I have the numeric vector template class below (vector for numerical calculations). I am trying make it possible to write D=A+B+C
where all variables are Vector
objects. A
, B
and C
should not be modified. My idea is to use Vector operator+(Vector&& B)
so that after (hopefully) an Rvalue Vector
has been returned from B+C
, all subsequent additions are stored in that object i.e. steal the storage of the Rvalue for all subsequent additions. This is in order to eliminate creation of new objects and required storage.
My problem is that I can see from output statements from each function called that Vector operator+(Vector&& B)
is never called. I cannot understand why since 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 hoped.
Sorry for the long winded question but this is my first question here and I want to try to be as clear as possible.
Any suggestions as to what I am obviously doing wrong or why I should not be trying this, would 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;
}
I would suggest that you provide
operator+=
as a member method and then reuse that from a free functionoperator+
defined as:Given a call
a + b + c
, which groups as(a+b) + c
, the compiler will create a copy ofa
for the first call tooperator+
, modify that in place (lhs += rhs
) and then move it into the returned value. It will then move that returned value (unless it elides the move) into the argument of the secondoperator+
, where it will be modified in place again, and then moved to the return value.Overall, a single new object will be created, holding the result of
a+b+c
, providing semantics equivalent to:But with the nicer, more compact syntax
a + b + c
.Note that this will not handle
a + (b+c)
graciously, and will create two objects, if you want to support that, you will need to produce multiple overloads.In the expression:
A
andB
are lvalues, so the callA+B
callsVector::operator(const Vector&)
That returns an rvalue, let's call it
tmp
, so the next sub-expression istmp+C
.C
is also an lvalue, so it callsVector::operator(const Vector&)
again. That returns another rvalue, lets call ittmp2
The final sub-expression is
D=tmp2
, but your type doesn't have a move-assignment operator, so the implicitly-defined copy-assignment operator is used.i.e. you never invoke
operator+
with an rvalue on the right-hand side, and the only expression which does have an rvalue argument is an assignment which you haven't defined for rvalues.It would be better to define overloaded non-member operators:
This will work for any combination of rvalues and lvalues. (In general
operator+
should usually be a non-member anyway.)Edit: the alternative suggestion below doesn't work, it results in ambiguities in some cases.
Another alternative, if your compiler supports it (I think only clang does,) would be to keep your existing
Vector::operator+(Vector&&)
but replace yourVector::operator+(const Vector&)
with two overloads distinguished by a ref-qualifier:This reuses
*this
when it's known to be an rvalue, i.e. it uses move semantics when the left-hand side of the addition is an rvalue, compared to your original code which can only use move semantics when the right-hand side is an rvalue. (N.B. the code above assumes you've defined a memberoperator+=
as suggested in David Rodriguez's answer)