Reputation: 13
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
Upvotes: 0
Views: 255
Reputation: 2965
Let’s go through these one by one:
auto i = f(1); // (1)
This creates a local variable i
(not a reference) initialized with the return value of f(1)
. This is OK. The lifetime of i
is until the end of the block.
p && j = f(2); // (2)
This initializes the reference j
with a reference to the object returned by f(2)
, and this extends the lifetime of the value returned by f(2)
, so this is OK. The lifetime of j
is until the end of the block.
p && k = move(f(3)); // (3)
This initializes the reference with a reference to the object returned by move
, no lifetime extension occurs since the value returned by move
is a reference to the temporary returned by f(2)
(and not a temporary object), but the temporary returned by f(2)
dies as soon as k
is initialized, since its lifetime is not extended (it’s not assigned to a reference variable), and k
ends up being a dangling reference.
auto l = move(f(4)); // (4)
Same as (1), the move is useless. The lifetime of l
is until the end of the block.
p ptr = make_shared<A>(5); // (5)
This is a local variable initialization. The lifetime of ptr
is until the end of the block.
auto && a = move(ptr);
a
is a reference to ptr
. This does not change the lifetime of ptr
.
Upvotes: 2
Reputation: 30494
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.
Upvotes: 2
Reputation: 54325
Because by using std::move
you've turned the return value into a reference. And not a const reference, so it's a temporary.
So no copy takes place and as soon as the line ends, the return value is destroyed.
Upvotes: 1