I'm watching the "Don’t Help the Compiler" talk by STL, where he has a similar example on slide 26:
struct A
{
A() = default;
A(const A&) { std::cout << "copied" << std::endl; }
A(A&&) { std::cout << "moved" << std::endl; }
};
std::pair<A, A> get_pair()
{
std::pair<A, A> p;
return p;
}
std::tuple<A, A> get_tuple()
{
std::pair<A, A> p;
return p;
}
std::tuple<A, A> get_tuple_moved()
{
std::pair<A, A> p;
return std::move(p);
}
With this, the following call:
get_pair();
get_tuple();
get_tuple_moved();
Produces this output:
moved
moved
copied
copied
moved
moved
Result of get_pair
is move-constructed, which is as expected. A move may also has been completely elided by NRVO, but it is off the topic of the present question.
Result of get_tuple_moved
is also move-constructed, which is explicitly specified to be so. However, result of get_tuple
is copy-constructed, which is completely un-obvious to me.
I thought that any expression passed to return
statement may be thought of as having implicit move
on it, since the compiler knows it is about to go out of scope anyway. Seems like I'm wrong. Can someone clarify, what is going on here?
See also related, but different question: When should std::move be used on a function return value?