When do you need to explicitly call std::move and when not in cpp?

I’m working through Stroustrup’s “Tour of C++ v2”. It’s certainly not a C++ beginner’s book, but enjoyable.

I’ve had a google and look through SO but no joy on this one.

Now, I thought I understood when the compiler can utilise a move constructor, but clearly I don’t. Here I show the move constructor and the function that I thought would use it. It doesn’t. Only if I explicitly use std::move. Why is this? My understanding was that the local r would be “moved” implicitly on return.

template<typename T>
Vector<T>::Vector(Vector<T> && a) // move constructor
     :elem{a.elem},sz{a.sz}{
     a.elem=nullptr;
     a.sz=0;
}

template<typename T>
Vector<T> moveVectorAfterAdd(const Vector<T> &  v1, const Vector<T> & v2){
     Vector<T> r =   v1+v2;
     return std::move(r);
     //return r;
}

int main(void) {
     Vector<double> v1(1);
     Vector<double> v2=v1;
     Vector<double> v3=v2;

     Vector<double> v4=moveVectorAfterAdd(v1,v2);

     return 0;
}

(As a side note, lldb won’t let me even set a break point in the move constructor despite compiling with no optimizations if I don’t actually use std::move.)

Any and all clarifications gladly received!

Answer

When do you need to explicitly call std::move and when not in cpp?

In short, and technically precise words: Use std::move when you have an lvalue that you want to be an rvalue. More practically: You would want to do that when there is a copy that you want instead to be a move. Hence the name std::move.

In the example, you return an automatic variable. There is no copy that can be avoided by using std::move because in the special case of returning an automatic variable, there will be a move even from an lvalue.

Here I show the move constructor and the function that I thought would use it. It doesn’t.

Just because there is a move in the abstract machine, doesn’t necessarily mean that there would be a call to the move constructor. This is a good thing because doing nothing can potentially be faster than calling the move constructor.

This is known as (Named) Return Value Optimization. Or more generally copy elision. Using std::move inhibits this optimization, so not only is it unnecessary in this case, but it is also counter productive.