58

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?

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
iampranabroy
  • 1,716
  • 1
  • 15
  • 11
  • 4
    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. – Some programmer dude Oct 11 '14 at 19:41
  • 4
    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). –  Oct 11 '14 at 19:59

2 Answers2

52

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

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • 1
    Is it better to change the constructor to take an r-value reference to unique_ptr? E.g. House(std::unique_ptr&& pet). Then you wouldn't need the std::move() call in the initializer list for pet_? – jfritz42 Nov 16 '16 at 00:15
  • 4
    @jfritz42 An r-value reference is itself an l-value so you still need the `std::move`. [Try it](http://coliru.stacked-crooked.com/a/42b8222989a16651). – Chris Drew Nov 16 '16 at 07:49
  • 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"? – jfritz42 Nov 16 '16 at 19:25
  • Note that you can also do move assignment: `uptr1 = std::move(uptr2)` – Shital Shah Apr 23 '18 at 23:53
7

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 .

user2485710
  • 9,451
  • 13
  • 58
  • 102
  • 1
    `unique_ptr` transfers ownership of the managed object when you `move` into another `unique_ptr`. – Jonathan Potter Oct 12 '14 at 00:43
  • @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 . – user2485710 Oct 12 '14 at 06:58
  • 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`? – Jonathan Potter Oct 12 '14 at 07:35
  • 2
    @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. – Chris Drew Oct 12 '14 at 13:06
  • @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` . – user2485710 Oct 12 '14 at 13:11
  • 2
    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. – Chris Drew Oct 12 '14 at 13:22
  • @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` ? – user2485710 Oct 12 '14 at 13:24
  • 1
    I'm not suggesting passing by const reference. I'm suggesting pass by value or rvalue-ref. – Chris Drew Oct 12 '14 at 13:37