1

I know that when returning a local in order to keep RVO we should allow the compiler to perform move elision by returning a value like so

std::vector<double> some_func(){
    std::vector<double> my_vector;
    //do something
    return my_vector;
}

rather than

std::vector<double> some_func(){
    std::vector<double> my_vector;
    //do something
    return std::move(my_vector);
}

However, if we are constructing an object in a return statement, do uses of std::move in the constructor either help, hinder, or do nothing to get efficiency or get in the compilers way?

As an example consider these two implementations

std::pair<std::vector<double>,std::vector<double> > some_func()
    std::vector<double> my_vec_a;
    std::vector<double> my_vec_b;
    //do something
    return std::make_pair(my_vec_a,my_vec_b);
}

and

std::pair<std::vector<double>,std::vector<double> > some_func()
    std::vector<double> my_vec_a;
    std::vector<double> my_vec_b;
    //do something
    return std::make_pair(std::move(my_vec_a),std::move(my_vec_b));
}

So

  1. In the first, will the compiler recognise that my_vec_a and my_vec_b will go out of scope and optimise the construction in the return statement?
  2. If so will the use of std::move in the second prevent that optimisation?
  3. Or will the second option be more efficient?
user3353819
  • 911
  • 2
  • 8
  • 21
  • The answer depends on the compiler and its optimization settings. Note that gcc 11.1 with -O1 produces exactly identical assembly even for your first two snippets. You can easily check it in your browser, here https://godbolt.org/ – DarioP Oct 14 '21 at 16:33
  • @DarioP Cool site, thanks. Yeah it all gives the same assembly when optimised. My main fear was getting in the way of elision somehow. – user3353819 Oct 14 '21 at 16:46
  • @user3353819 How do you figure that it gives the same assembly? Both compilers are able to better optimize for the case where `std::move` is used. https://godbolt.org/z/sa9ocnPvG. NRVO doesn't apply in this case as according to cppreference `which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO`. So, I'd prefer using `std::move` here. – Staz Oct 14 '21 at 16:54
  • @Staz Ah, I didn't wrap it in a `foo()` function so didn't see the upstream optimisation for the whole of the return, so was just looking at changes in the assembly for `some_func`. Yep, ok, understood, thanks. – user3353819 Oct 14 '21 at 17:02

1 Answers1

1

This one is more efficient:

std::pair<std::vector<double>,std::vector<double> > some_func() { 
    std::vector<double> my_vec_a;
    std::vector<double> my_vec_b;
    //do something
    return std::make_pair(std::move(my_vec_a),std::move(my_vec_b));
}

without the calls to std::move the two vectors will be copied.

The problem, however, isn't the actual return. It's that the two vectors in the pair being constructed are being constructed from lvalues; the compiler can't choose to use a move-constructor there without you explicitly telling it to via std::move.

jwezorek
  • 8,592
  • 1
  • 29
  • 46
  • 1
    There is another option: `std::pair, std::vector> r;\nauto& [my_vec_a, my_vec_b] = r; \n//do something\nreturn r;` – Deduplicator Oct 14 '21 at 18:53