Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transferring sole ownership: unique_ptr vs move semantics

It seems like std::unique_ptr solves a problem that can also be solved with move-semantics, i.e. transferring ownership of a uniquely-owned resource. Here are some examples where they seem to perform the same job:

class SomeResource { /* resourcey stuff */ };

void ProcessResourcePtr(std::unique_ptr<SomeResource> r) { /* do stuff */ }
void ProcessResourceRvalRef(SomeResource&& r) { /* do stuff */ }

class OwnerOfResourcePtr {
private:
    std::unique_ptr<SomeResource> r_;
public:
    OwnerOfResourcePtr(std::unique_ptr<SomeResource> r) : r_(std::move(r)) { }
};

class OwnerOfResourceRvalRef {
private:
    SomeResource r_;
public:
    OwnerOfResourceRvalRef(SomeResource&& r) : r_(std::move(r)) { }
};


int main()
{
    // transfer ownership to function via unique_ptr
    std::unique_ptr<SomeResource> r1(new SomeResource());
    ProcessResourcePtr(std::move(r1));

    // transfer ownership to function via rvalue ref
    SomeResource r2;
    ProcessResourceRvalRef(std::move(r2));

    // transfer ownership to instance via unique_ptr
    std::unique_ptr<SomeResource> r3(new SomeResource());
    OwnerOfResourcePtr(std::move(r3));

    // transfer ownership to instance via rvalue ref
    SomeResource r4;
    OwnerOfResourceRvalRef(std::move(r4));

    return 0;
}

To my eye, these both solve almost exactly the same problem in slightly different ways. I'm not 100% clear on the advantages of one way versus the other. I know that pointer moves are probably faster than move constructors/assignments, although both are usually assumed to be quite efficient. I'm also aware that move semantics allow you to keep your data on the stack (see r2, r4) instead of requiring a heap allocation/deallocation with new/malloc/etc (see r1, r3), which I think is a good thing (is it?).

In general when should one prefer a unique_ptr over move semantics, or vice-versa? Are there any use cases that can only be solved by one or the other?

like image 443
Brian Putnam Avatar asked Oct 23 '14 01:10

Brian Putnam


People also ask

What happens when you move a unique_ptr?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.

How do I transfer ownership of unique PTRS?

In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.

Is unique_ptr null after move?

Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal. This is clearly true after calling release(). But std::move doesn't call release(). So how does the compiler know to restore the invariant of the unique_ptr?

Can you assign a unique_ptr?

It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };


1 Answers

If you have a class (perhaps written by someone else) which does not have a move constructor, perhaps not even a copy constructor, then you may have to use unique_ptr.

If an instance is optional, i.e. may not be present, you should use unique_ptr.

If you can only initialize an object after your constructor is called, and the object is not default-constructible, you must delay construction, and can use unique_ptr for this.

Even if a class has a move constructor, that might be more expensive than moving a single pointer. For example, if the class contains dozens of POD instances, or an array.

like image 167
John Zwinck Avatar answered Sep 28 '22 07:09

John Zwinck