149

Does C++11 standard library provide any utility to convert from a std::shared_ptr to std::unique_ptr, or vice versa? Is this safe operation?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Hind Forsum
  • 9,717
  • 13
  • 63
  • 119
  • Define "safe operation" please. What sort of safety are you looking for? lifetime management safety? Thread safety? – jaggedSpire Jun 17 '16 at 15:05
  • 2
    "STL" doesn't mean standard library. The STL has nothing to do with `shared_ptr`. – curiousguy Dec 25 '16 at 01:39
  • 1
    @jaggedSpire Thread safety would mean you have owners used in different threads, i.e. the use count is not 1. – curiousguy Dec 25 '16 at 01:41
  • @curiousguy I knew that. My point was "safety" was not well defined in OP's question, and he needed to clarify which kind of "safety" he meant as there are multiple kinds. – jaggedSpire Dec 25 '16 at 03:17

3 Answers3

248

std::unique_ptr is the C++11 way to express exclusive ownership, but one of its most attractive features is that it easily and efficiently converts to a std::shared_ptr.

This is a key part of why std::unique_ptr is so well suited as a factory function return type. Factory functions can’t know whether callers will want to use exclusive ownership semantics for the object they return or whether shared ownership (i.e., std::shared_ptr) would be more appropriate. By returning a std::unique_ptr, factories provide callers with the most efficient smart pointer, but they don’t hinder callers from replacing it with its more flexible sibling.

std::shared_ptr to std::unique_ptr is not allowed. Once you’ve turned lifetime management of a resource over to a std::shared_ptr, there’s no changing your mind. Even if the reference count is one, you can’t reclaim ownership of the resource in order to, say, have a std::unique_ptr manage it.

Reference: Effective Modern C++. 42 SPECIFIC WAYS TO IMPROVE YOUR USE OF C++11 AND C++14. Scott Meyers.

In short, you can easily and efficiently convert a std::unique_ptr to std::shared_ptr but you cannot convert std::shared_ptr to std::unique_ptr.

For example:

std::unique_ptr<std::string> unique = std::make_unique<std::string>("test");
std::shared_ptr<std::string> shared = std::move(unique);

or:

std::shared_ptr<std::string> shared = std::make_unique<std::string>("test");
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
chema989
  • 3,962
  • 2
  • 20
  • 33
  • Be aware that while not allowed the compiler (at least not gcc) will actually *not* prevent (or even warn) if you accidentally (e.g. by changing the pointer type of a member variable) assign a `std::unique_ptr` to a `std::shared_ptr`. – StefanQ Feb 20 '20 at 10:17
  • STL reference: https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr – Ida Sep 17 '20 at 09:38
  • 6
    @StefanQ What makes you think that it's not allowed to assign a `std::unique_ptr` into a `std::shared_ptr`? The standard library defines a move assignment operator `template shared_ptr& operator=(std::unique_ptr&& r);` for `std::shared_ptr`. – cuddlebugCuller Oct 18 '20 at 02:27
  • I want to highlight that the conversion from `unique_ptr` to `shared_ptr` works because `shared_ptr` class has knowledge of `unique_ptr` and it is equipped with both constructor and assignment operator that accept a `rvalue reference` to `unique_ptr` (the rvalue reference is essential because guarantees the `unique_ptr` is not going to be used anymore). It is not `std::move` that makes the magic, but `shared_ptr` class. – luca Feb 12 '22 at 11:18
2

I would prefer to do this.

std::unique_ptr<int> up_ = std::make_unique<int>();
std::shared_ptr<int> sp_ = std::move(up_);
pkthapa
  • 1,029
  • 1
  • 17
  • 27
-10

Given unique_ptr u_ptr, create shared_ptr s_ptr:

std::shared_ptr<whatever> s_ptr(u_ptr.release());

Going the other way is impractical.

(As many people have noted, there is more to this story, and it's worth reading the comments.)

nmr
  • 16,625
  • 10
  • 53
  • 67
  • 37
    Here's the "right" way: `std::shared_ptr s_ptr(std::move(u_ptr));` – Emil Laine Dec 25 '16 at 01:55
  • 13
    And here is the pedantic "right" way: `std::shared_ptr s_ptr{std::move(u_ptr)};` – polyvertex Nov 20 '17 at 17:06
  • @polyvertex: is there any actual difference between these two syntaxes in this particular case? – Violet Giraffe Mar 11 '18 at 22:24
  • 1
    @nmr's conversion is not wrong, just a bit not safer, so don't downvote his answer. I consider emlai's conversion the "right" way. – Stoica Mircea Jul 17 '18 at 12:18
  • 4
    What's less safe about it? – nmr Jul 17 '18 at 20:56
  • 10
    @VioletGiraffe • I suppose polyvertex is advocating using the new initialization list syntax — which avoids silent narrowing conversions and accommodates member initialization with a uniform syntax — as a good habit to get into. Six of one, half-dozen of the other? – Eljay Aug 16 '18 at 11:57
  • Initialization in C++ is a big fat mess, and it's finally getting fixed in C++20. We should all use the new uniform way. See: https://www.youtube.com/watch?v=9-_TLTdLGtc – Zendel Jan 24 '19 at 18:59
  • 17
    @nmr It is unsafe because you may lose the `Deleter` stored inside the `unique_ptr` – Zang MingJie Aug 19 '19 at 08:56
  • Would be the only pre C++11 way though, wouldn't it be? – Jimmy R.T. Mar 17 '20 at 11:53
  • @JimmyR.T. `shared_ptr` and `unique_ptr` were introduced with C++11. – Olaf Dietsche Mar 20 '20 at 10:57
  • @ZangMingJie Why "may lose" and not just "lose" ? I see no reason for compilers to recognize the intent and forward the deleter to the shared pointer in that situation. IMHO the custom deleter gets simply lost with nmr's solution, which is why the answer is not just "a bit not safer", but rather "dangerous" – Norman Pellet Jul 09 '20 at 13:51
  • 3
    After reading the comments I believe we can fix the answer with: std::shared_ptr s_ptr(u_ptr.release(), u_ptr.get_deleter()); – luca Dec 15 '21 at 16:43
  • 3
    @nmr this answer is very useful, if nothing else for the interesting discussion in the comments. I will upvote it, maybe it would be useful to add a note in the actual answer regarding the deleter? – luca Dec 15 '21 at 16:46