I got some code look like this
struct A
{
int i;
A(int i) : i(i) {}
~A()
{
cout << "destroy " << i << endl;
}
};
using p = shared_ptr<A>;
p f(int i)
{
return make_shared<A>(i);
}
int main()
{
auto i = f(1);
cout << "a" << endl;
p && j = f(2);
cout << "a" << endl;
p && k = move(f(3));
cout << "a" << endl;
auto l = move(f(4));
cout << "a" << endl;
p ptr = make_shared<A>(5);
auto && a = move(ptr);
cout << "a" << endl;
}
and the output is
a
a
destroy 3
a
a
a
destroy 5
destroy 4
destroy 2
destroy 1
I don't understand why move
a function's return value to a rvalue reference cause destruction. But put it directly to a rvalue reference is ok.
The problem is actually found with std::get<>
of a std::tuple
. I have a function that return a tuple of two shared_ptr and use std::get<> to access the element. But I found that auto && i = get<0>(f())
will cause error but auto i = get<0>(f())
won't. Then I find a similar but simpler situation for std::move
p && j = f(2);
Here, f
returns a prvalue of type std::shared_ptr<A>
. When you bind a reference to a prvalue it extends the lifetime of the prvalue to be that of the reference. That means that the object returned by f
will have the same lifetime as j
.
p && k = move(f(3));
Here, once again f
returns a prvalue. std::move
's parameter binds to that prvalue, extending its lifetime to the lifetime of the parameter. std::move
does not return a prvalue though. It returns an xvalue. Lifetime extension does not apply to xvalues, so the object returned by f
gets destroyed as soon as std::move
's parameter's lifetime ends. That is, it gets destroyed when std::move
returns. k
then becomes a dangling reference.