Transferring the ownership of object from one unique_ptr to another unique_ptr in C++11?

54,419

Solution 1

The following situations involve transferring ownership from one unique_ptr to another: returning from a function, and passing as a parameter to a function like a constructor.

Say you have some polymorphic type Animal:

struct Animal {
  virtual ~Animal() {}
  virtual void speak() = 0;
};

with concrete subclasses Cat and Dog:

struct Cat : Animal {
  void speak() override { std::cout << "Meow!\n"; }
};

struct Dog : Animal {
  void speak() override { std::cout << "Woof!\n"; }
};

And you want a simple factory that creates a pet based on a required value of obedience. Then the factory must return a pointer. We want the pet factory to transfer ownership of the created pet to the caller so a reasonable return type is std::unique_ptr<Animal>:

std::unique_ptr<Animal> createPet(double obedience) {
  if (obedience > 5.0)
    return std::make_unique<Dog>();
  return std::make_unique<Cat>();
} 

Now, say we want to create a House that will own the pet then we might want to pass the pet into the constructor of the House. There is some debate (see comments on this blog post) about how best to pass a unique_ptr to a constructor but it would look something like this:

class House {
 private:
  std::unique_ptr<Animal> pet_;
 public:
  House(std::unique_ptr<Animal> pet) : pet_(std::move(pet)) {}
};

We have passed the unique_ptr into the constructor and have then "moved" it to the member variable.

The calling code could look something like:

  auto pet = createPet(6.0);
  House house(std::move(pet));

After constructing the House, the pet variable will be nullptr because we have transferred ownership of the pet to the House.

Live demo

Solution 2

for example if you call a function you can move your unique_ptr in the parameter list so it can be a part of your function signature

foo ( std::unique_ptr<T>&& ptr )

you can call foo with

foo( std::move(myPtr) );

Note that std::move is an unconditional cast and unique_ptr is an object with a state, and a part of that state is the pointer that that unique_ptr is managing, with std::move you are casting the entire object, you are not really changing anything about ownership, there is nothing peculiar about std::unique_ptr while using std::move because std::move doesn't really care about anything specific, as I said it is an unconditional cast and unique_ptr simply gets casted, the entire object that is an instance of type unique_ptr<T> is casted .

If you want to talk about a transfer of ownership of the object pointed by your unique_ptr, you should consider the swap provided by std::unique_ptr<T> itself .

Share:
54,419

Related videos on Youtube

iampranabroy
Author by

iampranabroy

Software developer with proficient knowledge in Microservices, Java, Kubernetes, AWS.

Updated on July 09, 2022

Comments

  • iampranabroy
    iampranabroy almost 2 years

    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.

    std::unique_ptr<int> p1(new int(42));
    std::unique_ptr<int> p2 = std::move(p1); // Transfer ownership
    

    What are the situations where this will be useful as it is transferring the ownership to another unique_ptr?

    • Some programmer dude
      Some programmer dude over 9 years
      That's it. The smart pointers in the standard library should not really be seen as pointers that are automatically free'd, but in terms of ownership. Do you have some data that can only be "owned" by a single entity, then use unique pointers.
    • Admin
      Admin over 9 years
      It's useful when you are working with non-copyable data like threads or sockets and you need to replace it from one place to another (for example, to put it into the vector).
  • Jonathan Potter
    Jonathan Potter over 9 years
    unique_ptr transfers ownership of the managed object when you move into another unique_ptr.
  • user2485710
    user2485710 over 9 years
    @JonathanPotter that's semantically incorrect, move doesn't do anything, it's just a cast, its semantic is about mutating an expression into an rvalue, non about trasferring ownership in the the case of unique_ptr, when the function call will happen, that unique_ptr will be moved, that's a side effect and if your purpose is to change ownership this is the most cryptic way of doing it with the wrong semantic, just don't do it, use swap, it has the right semantic and it just does what you want .
  • Jonathan Potter
    Jonathan Potter over 9 years
    To be honest I'm not quite sure what you mean. But what do you think happens when you push_back a unique_ptr into a vector using move?
  • Chris Drew
    Chris Drew over 9 years
    @user1308004, whilst it is true that std::move doesn't actually move, it just prepares it to be moved from, that distinction is not important in most cases hence why they decided to call it std::move. I don't see what is cryptic about using std::move to a transfer ownership from one unique_ptr into another. That's what unique_ptr has a move constructor and move assignment for.
  • user2485710
    user2485710 over 9 years
    @ChrisDrew is cryptic because std::move is general, it's in the std namespace ready for everyone to use, it's also a cast, which means it doesn't express any intent beside a change of type. Don't you think that swap is like a magnitude better than a generic cast ? swap does what it says it does, it's just designed to do that, has the right semantics and there is even a specialized swap for unique_ptr .
  • Chris Drew
    Chris Drew over 9 years
    If you want to use swap to transfer ownership in a function call you have to pass by non-const reference which does not express intent at all. Passing by value or rvalue-ref much better expresses the intent.
  • user2485710
    user2485710 over 9 years
    @ChrisDrew what a const reference of an unique_ptr gives you ? It just prevents possible optimizations, why you even need const when your are using unique_ptr ?
  • Chris Drew
    Chris Drew over 9 years
    I'm not suggesting passing by const reference. I'm suggesting pass by value or rvalue-ref.
  • jfritz42
    jfritz42 over 7 years
    Is it better to change the constructor to take an r-value reference to unique_ptr? E.g. House(std::unique_ptr<Animal>&& pet). Then you wouldn't need the std::move() call in the initializer list for pet_?
  • Chris Drew
    Chris Drew over 7 years
    @jfritz42 An r-value reference is itself an l-value so you still need the std::move. Try it.
  • jfritz42
    jfritz42 over 7 years
    Good point! BTW this seems like a new C++ idiom. I wonder if it's been given a name by anybody yet. The "transfer ownership idiom"?
  • Shital Shah
    Shital Shah about 6 years
    Note that you can also do move assignment: uptr1 = std::move(uptr2)