I liked std::unique_ptr the moment I saw it back in c++11, but I questioned it's effectiveness for quite a while. (see link below for link to live code) :
#include <memory>
std::unique_ptr<int> get();
extern std::unique_ptr<int> val;
void foo()
{
val = get();
}
This gave me 16 instructions on last clang with -O3. But what is more interesting is that it generated two calls to delete, even though the second one will never be called.
Than I tried to do it like this:
void foo()
{
auto ptr = get().release();
val.reset(ptr);
}
And suddenly it's just 11 instructions. Then I went deeper and hacked unique_ptr move ctor. Originally it is implemented as reset(__u.release());. I basically just reordered it as follows:
auto& ptr = _M_ptr();
if (ptr)
_M_deleter()(ptr);
ptr = __u.release();
Aaand.... 11 instructions as in hand-managed version. It is slightly different but seems ok.
I saved my experiments here.
Can someone point out is that it's I'm missing something or it's actually somehow intended?
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
std::unique_ptr::reset Destroys the object currently managed by the unique_ptr (if any) and takes ownership of p. If p is a null pointer (such as a default-initialized pointer), the unique_ptr becomes empty, managing no object after the call.
unique_ptr. An unique_ptr has exclusive ownership of the object it points to and will destroy the object when the pointer goes out of scope.
Because the unique pointer does not have a copy constructor. Hence you cannot pass it by value, because passing by value requires making a copy. Actually that is nearly the sole and whole point of a unique_ptr .
The order of operations for move-assignment must be:
Note that this logic happily results in a no-op for self-move-assignment.
The reason the logic must be this way is the source may be owned by the destination.
Imagine:
struct list { std::unique_ptr<list> next; };
std::unique_ptr<list> head;
// ...
if (head) head = std::move(head->next);
This will not behave correctly if head->next's managed pointer isn't nulled before deleting the old object managed by head.
In code, move assignment therefore is just:
reset(source.release())
Your last snippet obviously fails to null the source before deletion, and is thus not a viable implementation for move assignment:
auto& ptr = _M_ptr();
if (ptr)
_M_deleter()(ptr);
ptr = __u.release();
That leaves your question about why
val = get();
is different than
auto ptr = get().release();
val.reset(ptr);
The difference is in the implicit unique_ptr<int> returned from get(). In the first version, it is destroyed after both the release and reset. In the second version it is destroyed after release but before reset. In both cases, it is nulled by the time it is destroyed, and there need not be a delete. But the compiler must be unable to propagate the knowledge that this pointer was nulled across the reset.
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