When I move a unique_ptr<>
into a function that throws, the moved-from pointer is left non-null. Is this the normal behavior?
Here's two test programs that show the behavior. I can observe the unique pointer in the destructor of the class:
#include <memory>
#include <iostream>
void f(std::unique_ptr<int> &&) { throw "fail"; }
struct except_move_tester
{
std::unique_ptr<int> x;
except_move_tester()
: x(std::make_unique<int>(0))
{}
~except_move_tester() { std::cout << "x at destructor: " << x.get() << std::endl; }
void g()
{
std::cout << "x at g: " << x.get() << std::endl;
f(std::move(x));
}
};
int main()
{
try {
except_move_tester t;
t.g();
} catch (...) {}
}
which when run gives the output:
x at g: 0x7f818b402ac0
x at destructor: 0x7f818b402ac0
If I modify the above listing as follows (just added a temporary at the site of the function call) I get the exception safe behavior I would normally expect:
#include <memory>
#include <iostream>
void f(std::unique_ptr<int> &&) { throw "fail"; }
struct except_move_tester
{
std::unique_ptr<int> x;
except_move_tester()
: x(std::make_unique<int>(0))
{}
~except_move_tester() { std::cout << "x at destructor: " << x.get() << std::endl; }
void g()
{
std::cout << "x at g: " << x.get() << std::endl;
auto y = std::move(x);
f(std::move(y));
}
};
int main()
{
try {
except_move_tester t;
t.g();
} catch (...) {}
}
which when run gives the output:
x at g: 0x7f818b402ac0
x at destructor: 0x0
I've been moving unique_ptr's into functions under the assumption it was a sort of atomic exception safe operation, but this seems to indicate that an exception can leave a unique pointer in an unexpected state.
std::move
just converts the object to rvalue, but won't perform move operation.
(emphasis mine)
std::move
is used to indicate that an objectt
may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.In particular,
std::move
produces an xvalue expression that identifies its argument t. It is exactly equivalent to astatic_cast
to an rvalue reference type.
Your 2nd snippet works because you moved x
into y
explicitly.
To fix the 1st snippet you can also perform the move operation explicitly, e.g.
void f(std::unique_ptr<int> && p) {
std::unique_ptr<int> t = std::move(p); // move-construct t from p
throw "fail";
}
or just
void f(std::unique_ptr<int> p) { throw "fail"; }
For the latter, given f(std::move(x));
, the parameter p
is moved from the argument x
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With