If you look at get
, the helper function for std::tuple
, you will notice the following overload:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t );
In other words, it returns an rvalue reference when the input tuple is an rvalue reference itself. Why not return by value, calling move
in the function body? My argument is as follows: the return of get will either be bound to a reference, or to a value (it could be bound to nothing I suppose, but this shouldn't be a common use case). If it's bound to a value, then a move construction will anyway occur. So you lose nothing by returning by value. If you bind to a reference, then returning an rvalue reference can actually be unsafe. To show an example:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double;
}
When run, this program prints:
Constructed at : 0x7ffc0e12cdc0
Destructed at : 0x7ffc0e12cdc0
0
In other words, x is immediately a dangling reference. Whereas if you just wrote foo like this:
struct foo {
Hello m_hello;
Hello get() && { return std::move(m_hello); }
};
This problem would not occur. Furthermore, if you then use foo like this:
Hello x(foo().get());
It doesn't seem like there is any extra overhead whether you return by value, or rvalue reference. I've tested code like this, and it seems like it will quite consistently only perform a single move construction. E.g. if I add a member:
Hello(Hello && ) { std::cerr << "Moved" << std::endl; }
And I construct x as above, my program only prints "Moved" once regardless of whether I return by value or rvalue reference.
Is there a good reason I'm missing, or is this an oversight?
Note: there is a good related question here: Return value or rvalue reference?. It seems to say that value return is generally preferable in this situation, but the fact that it shows up in the STL makes me curious whether the STL has ignored this reasoning, or if they have special reasons of their own that may not be as applicable generally.
Edit: Someone has suggested this question is a duplicate of Is there any case where a return of a RValue Reference (&&) is useful?. This is not the case; this answer suggests return by rvalue reference as a way to elide copying of data members. As I discuss in detail above, copying will be elided whether you return by value or rvalue reference provided you call move
first.