Most C++ standard library utilities return by rvalue reference when overloaded on a rvalue qualifier for this. For example std::optional has the following overloads for the value()
function
constexpr T& value() &;
constexpr const T & value() const &;
constexpr T&& value() &&;
constexpr const T&& value() const &&;
This allows the returned value to be moved from when needed, good. This is a solid optimization.
But what about the uncertainty associated with the returning of an rvalue? For example (live example here https://wandbox.org/permlink/kUqjfOWWRP6N57eS)
auto get_vector() {
auto vector = std::vector<int>{1, 2, 3};
return std::optional{std::move(vector)};
}
int main() {
for (auto ele : *get_vector()) {
cout << ele << endl;
}
}
The code above causes undefined behavior because of how the range based for loop is expanded
{
auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
The forwarding reference range
when binding to the return value of *get_vector()
does not extend the lifetime of the xvalue. And results in binding to a destroyed value. And therefore results in UB.
Why not return by value and internally move the stored object? Especially because now C++17 has the prvalue optimization, for example
auto lck = std::lock_guard{mtx};
Note that this is not the same as this question here C++11 rvalues and move semantics confusion (return statement), this does not mention the lifetime extension problem with rvalue returns with container/holders and was asked way before C++17 had mandatory elision for prvalues