When returning values from a function in C++, we have copy elision and (Named) Return Value Optimization helping us create more efficient code. In short, the following code:
std::vector<int> make_vec_1(){
std::vector<int> v;
v.resize(1e6);
return v;
}
results in a silent move or direct construction into the destination of the return value, instead of a copy. The rules around this also mean that explicitly moving the returned object when returning actually prevents these optimizations.
std::vector<int> make_vec_2(){
std::vector<int> v;
v.resize(1e6);
return std::move(v); // BAD
}
This version prevents RVO, as explained in Scott Meyers' Effective Modern C++, Item 25.
My question is what happens when the return type is different, but can be move-constructed from one or more local variables? Consider the following functions that each return an optional vector:
std::optional<std::vector<int>> make_opt_vec_1(){
std::vector<int> v;
v.resize(1e6);
return v; // no move
}
std::optional<std::vector<int>> make_opt_vec_2(){
std::vector<int> v;
v.resize(1e6);
return std::move(v); // move
}
Which of these is correct? The line return std::move(v) looks like a red flag to me at first, but I also suspect it's the correct thing to do here. The same goes for the following two functions returning a pair of vectors:
std::pair<std::vector<int>, std::vector<int>> make_vec_pair_1(){
std::vector<int> v1, v2;
v1.resize(1e6);
v2.resize(1e6);
return {v1, v2}; // no move
}
std::pair<std::vector<int>, std::vector<int>> make_vec_pair_2(){
std::vector<int> v1, v2;
v1.resize(1e6);
v2.resize(1e6);
return {std::move(v1), std::move(v2)}; // move
}
In this case too, despite looking weird at first glance, I think moving into the return value is the better thing to do.
Am I correct that it's better to move into the return value when the types differ, but the return value can be move constructed from the local variable(s) being moved from? Have I misunderstood NRVO, or is there some other optimization that is well ahead of me here?