9

I cannot figure out if the std::move in the following code does anything good or that it is completely wrong? The class Object has both Move and Copy constructor defined.

First: With Move:

template<typename T> template <typename F> 
const Object<T> Object<T>::operator*(const F& rhs) const 
{
    return std::move(Object(*this) *= rhs);  // We end in move constructor
}

Second: Without Move:

template<typename T> template <typename F> 
const Object<T> Object<T>::operator*(const F& rhs) const 
{
    return Object(*this) *= rhs; // We end in copy constructor
}

The *= operator is defined as:

template<typename T> template<typename F>  
Object<T>& Object<T>::operator*=(const F& rhs) 
{
    for(int i = 0; i < dimension ; i++)
    {
        _inner[i] *= rhs;
    }
    return *this;
}

Here is the code i use to test it:

Object<double> test(4);
Object<double> test2(test * 4);
std::cout << test2; // works fine

Result In the first case we end in the move constructor and in the second we end in the copy constructor.

In either case the code compiles.

Is one more efficient than the other since I would assume it is faster to move the new object out instead of copying it out?

Additional info: I use the following compiler: g++ (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3

CodeTower
  • 6,293
  • 5
  • 30
  • 54
  • 8
    Don't return `const` values, don't explicitly `std::move` local variables / temporaries, it inhibits (N)RVO. Why not write your `operator*` as `Object tmp(*this); tmp *= rhs; return tmp;`? That way, the compilers sees you return a local variable and turns it into an rvalue automatically when `return`ing it. – Xeo May 17 '13 at 09:34
  • Take a look at: http://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion – Steve May 17 '13 at 09:36
  • I just tested without `const`, it doesn't change the situation, but i could understand from the answers that i need `std::move` as i return a `lvalue`. If on the other hand it were and `rvalue` i didn't need `std::move`. And lastly if i used a temporary variable it would automatically be moved out? – CodeTower May 17 '13 at 10:04

1 Answers1

15

Is one more efficient than the other since I would assume it is faster to move the new object out instead of copying it out?

Yes, using std::move here will be more efficient, assuming the object has move semantics more efficient than copying.

Usually, when returning a temporary or a local variable, move semantics will be used automatically. However, in this case you're not directly returning the temporary, but rather the lvalue reference returned by operator*=. Since an lvalue won't be moved, you do need std::move in this case to turn it into an rvalue.

However, you should not return a const value, as this prevents the return value from being used to move-initialise (or move-assign to) another object. Your example will initialise test2 by copying the return value, although that copy might be elided.

Alternatively, you could implement it using a local variable:

template<typename T> template <typename F> 
Object<T> Object<T>::operator*(const F& rhs) const 
{
    Object lhs(*this);
    lhs *= rhs;
    return lhs;
}

Not only can the return value be moved, but the move itself can be elided.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • I suspect RVO will take care of the lvalue case so it should be indistinguishable (with perhaps even a slight advantage for RVO over move). But your last example (local variable) settles it all anyway. +1 – syam May 17 '13 at 10:04
  • 1
    @syam: I'm fairly sure elision is only allowed when (directly) returning a temporary or a local automatic variable. Certainly, my compiler doesn't elide the copy when returning the *lvalue*. – Mike Seymour May 17 '13 at 10:09
  • Please also advise to return a *non-`const`* `Object`, as that returned value will cause a copy rather than a move if it's used like `auto c = a * b;` – Xeo May 17 '13 at 10:15
  • @Xeo: Yes, I just noticed the `const` and added some advise about not doing that. (I didn't notice it in my tests, since that copy can be elided). – Mike Seymour May 17 '13 at 10:20