Does C++11 Unique_Ptr and Shared_Ptr Able to Convert to Each Other's Type

Does C++11 unique_ptr and shared_ptr able to convert to each other's type?

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");

shared_ptr and unique_ptr conversion

I think you are asking a kind of optimization question. You want Example to use unique_ptr because it has simpler and more efficient semantics (paraphrasing your referenced article). But, when the need arises, you wish to allow the pointer to be converted to shared_ptr.

Example should simply provide an interface for that, and itself needs to convert from unique_ptr to shared_ptr, when its user invokes that interface. You could use state pattern to capture whether the instance is in unique_ptr mode or shared_ptr mode.

class Example
{
struct StateUnique;
struct StateShared;
struct State {
State (std::unique_ptr<State> &s) : _state(s) {}
virtual ~State () = default;
virtual Node & getPtr () = 0;
virtual std::shared_ptr<Node> & getShared() = 0;
std::unique_ptr<State> &_state;
};
struct StateUnique : State {
StateUnique (std::unique_ptr<State> &s)
: State(s), _ptr(std::make_unique<Node>()) {}
Node & getPtr () { return *_ptr.get(); }
std::shared_ptr<Node> & getShared() {
_state = std::make_unique<StateShared>(*this);
return _state->getShared();
}
std::unique_ptr<Node> _ptr;
};
struct StateShared : State {
StateShared (StateUnique &u)
: State(u._state), _ptr(std::move(u._ptr)) {}
Node & getPtr () { return *_ptr.get(); }
std::shared_ptr<Node> & getShared() { return _ptr; }
std::shared_ptr<Node> _ptr;
};
public:
Example(): _state(std::make_unique<StateUnique>(_state)) {}
Node & getNode() { return _state->getPtr(); }
std::shared_ptr<Node> & getShared() { return _state->getShared(); }
private:
std::unique_ptr<State> _state;
};

If the state machine looks scary (which it should, since it is over-engineered), then you can just maintain two pointers in the Example, and your methods which need to test which one it needs to use.

class Example
{
public:
Example(): _u_node(std::make_unique<Node>()) {}
Node & getNode() { return _u_node ? *_u_node.get() : *_s_node.get(); }
std::shared_ptr<Node> & getShared() {
if (_u_node) _s_node = std::move(_u_node);
return _s_node;
}
private:
std::unique_ptr<Node> _u_node;
std::shared_ptr<Node> _s_node;
};

creating a shared_ptr from unique_ptr

UPDATE 2: This bug has been fixed in Clang in r191150. GCC rejects the code with a proper error message.


UPDATE: I have submitted a bug report. The following code on my machine with clang++ 3.4 (trunk 191037)

#include <iostream>
#include <memory>

int main()
{
std::unique_ptr<int> u_ptr(new int(42));

std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl;
std::cout << "*u_ptr = " << *u_ptr << std::endl;

auto s_ptr = std::make_shared<int>(std::move(u_ptr));

std::cout << "After move" << std::endl;

std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl;
std::cout << "*u_ptr = " << *u_ptr << std::endl;
std::cout << " s_ptr.get() = " << s_ptr.get() << std::endl;
std::cout << "*s_ptr = " << *s_ptr << std::endl;
}

prints this:

 u_ptr.get() = 0x16fa010
*u_ptr = 42
After move
u_ptr.get() = 0x16fa010
*u_ptr = 42
s_ptr.get() = 0x16fa048
*s_ptr = 1

As you can see, the unique_ptr hasn't been moved from. The standard guarantees that it should be null after it has been moved from. The shared_ptr points to a wrong value.

The weird thing is that it compiles without a warning and valgrind doesn't report any issues, no leak, no heap corruption. Weird.

The proper behavior is shown if I create s_ptr with the shared_ptr ctor taking an rvalue ref to a unique_ptr instead of make_shared:

#include <iostream>
#include <memory>

int main()
{
std::unique_ptr<int> u_ptr(new int(42));

std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl;
std::cout << "*u_ptr = " << *u_ptr << std::endl;

std::shared_ptr<int> s_ptr{std::move(u_ptr)};

std::cout << "After move" << std::endl;

std::cout << " u_ptr.get() = " << u_ptr.get() << std::endl;
//std::cout << "*u_ptr = " << *u_ptr << std::endl; // <-- would give a segfault
std::cout << " s_ptr.get() = " << s_ptr.get() << std::endl;
std::cout << "*s_ptr = " << *s_ptr << std::endl;
}

It prints:

 u_ptr.get() = 0x5a06040
*u_ptr = 42
After move
u_ptr.get() = 0
s_ptr.get() = 0x5a06040
*s_ptr = 42

As you see, u_ptr is null after the move as required by the standard and s_ptr points to the correct value. This is the correct behavior.


(The original answer.)

As Simple has pointed out: "Unless Foo has a constructor that takes a std::unique_ptr it shouldn't compile."

To expand on it a little bit: make_shared forwards its arguments to T's constructor. If T doesn't have any ctor that could accept that unique_ptr<T>&& it is a compile error.

However, it is easy to fix this code and get what you want (online demo):

#include <memory>
using namespace std;

class widget { };

int main() {

unique_ptr<widget> uptr{new widget};

shared_ptr<widget> sptr(std::move(uptr));
}

The point is: make_shared is the wrong thing to use in this situation. shared_ptr has a ctor that accepts an unique_ptr<Y,Deleter>&&, see (13) at the ctors of shared_ptr.

Differences between unique_ptr and shared_ptr

Both of these classes are smart pointers, which means that they automatically (in most cases) will deallocate the object that they point at when that object can no longer be referenced. The difference between the two is how many different pointers of each type can refer to a resource.

When using unique_ptr, there can be at most one unique_ptr pointing at any one resource. When that unique_ptr is destroyed, the resource is automatically reclaimed. Because there can only be one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error. For example, this code is illegal:

unique_ptr<T> myPtr(new T);       // Okay
unique_ptr<T> myOtherPtr = myPtr; // Error: Can't copy unique_ptr

However, unique_ptr can be moved using the new move semantics:

unique_ptr<T> myPtr(new T);                  // Okay
unique_ptr<T> myOtherPtr = std::move(myPtr); // Okay, resource now stored in myOtherPtr

Similarly, you can do something like this:

unique_ptr<T> MyFunction() {
unique_ptr<T> myPtr(/* ... */);

/* ... */

return myPtr;
}

This idiom means "I'm returning a managed resource to you. If you don't explicitly capture the return value, then the resource will be cleaned up. If you do, then you now have exclusive ownership of that resource." In this way, you can think of unique_ptr as a safer, better replacement for auto_ptr.

shared_ptr, on the other hand, allows for multiple pointers to point at a given resource. When the very last shared_ptr to a resource is destroyed, the resource will be deallocated. For example, this code is perfectly legal:

shared_ptr<T> myPtr(new T);       // Okay
shared_ptr<T> myOtherPtr = myPtr; // Sure! Now have two pointers to the resource.

Internally, shared_ptr uses reference counting to track how many pointers refer to a resource, so you need to be careful not to introduce any reference cycles.

In short:

  1. Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed.
  2. Use shared_ptr when you want multiple pointers to the same resource.

Overload method for unique_ptr and shared_ptr is ambiguous with polymorphism

The problem you are encountering is this constructor of shared_ptr (13), (which is not explicit), is as good a match as a similar "moving derived to base" constructor of unique_ptr (6) (also not explicit).

template< class Y, class Deleter > 
shared_ptr( std::unique_ptr<Y,Deleter>&& r ); // (13)

13) Constructs a shared_ptr which manages the object currently managed by r. The deleter associated with r is stored for future deletion of the managed object. r manages no object after the call.

This overload doesn't participate in overload resolution if std::unique_ptr<Y, Deleter>::pointer is not compatible with T*. If r.get() is a null pointer, this overload is equivalent to the default constructor (1). (since C++17)

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept; //(6)

6) Constructs a unique_ptr by transferring ownership from u to *this, where u is constructed with a specified deleter (E).

This constructor only participates in overload resolution if all of the following is true:

a) unique_ptr<U, E>::pointer is implicitly convertible to pointer

b) U is not an array type

c) Either Deleter is a reference type and E is the same type as D, or Deleter is not a reference type and E is implicitly convertible to D

In the non polymorphic case, you are constructing a unique_ptr<T> from a unique_ptr<T>&&, which uses the non-template move constructor. There overload resolution prefers the non-template


I'm going to assume that Scene stores shared_ptr<Interface>s. In that case you don't need to overload addObject for unique_ptr, you can just allow the implicit conversion in the call.

Constructing shared_ptr from unique_ptr with and without move?

Are the two above equivalent

Yes

could someone explain what this warning means and why it occurs in the 3rd example?

Copy Elison only works when the result of a function call returning a temp object is directly assigned to a variable. The compiler can optimize away the temp object and let the function initialize the variable directly. But in the 3rd example, there is an intermediate type-cast being performed between the construction of the temp object and the assignment of the temp object to a variable, so the temp object can't be optimized away.

std::unique_ptr and std::shared_ptr as parameters for virtual functions

If doSomethingWithUser don't need ownership, you shouldn't transfer it to this method.

Indeed, copying a shared pointer or moving a unique pointer effectively transfers the ownership of the resource.

Your functions parameters should reflect your intentions about the assumed ownership of the passed resources.

If you only need to observe the value, and maybe mutating it, you should pass a non-owning handle to your function.

If you need to keep the resource alive and maybe deleting it, then you should pass a owning handle, whether it's a shared it unique ownership.

In your case, the name of the function tells me that you need to "do something with the user", without containing it passed the caller's lifetime. So you should pass a non owning handle. That handle can be a User* or User&, and even a std::reference_wrapper<User> or a boost::optional<User&>, depending on your needs.

Your interface should indeed express what an object should be able to do, and indeed enforce what parameters should each function take, but your interface should also express what ownership it has over it's parameters.

I usually prefer references since they ensure it cannot be null and work well with object in automatic storage. Some may argue that they prefer raw pointers as a non-null non-owning handle, but I strongly disagree with that, because they force the &object syntax and allows null.

There is nothing wrong with non-owning raw pointers. However, raw owning pointers should not be used in modern C++.

Move ownership from std::shared_ptr to std::unique_ptr

Logically such a scenario doesn't make sense to me.

Suppose for a while that it is possible to transfer the ownership, but you could do that only when you're sure that there is only one shared_ptr alives; if that is the case, then you can still use shared_ptr as member of A and pretend that it is unique_ptr.

And then you commented:

That's true, I could create shared_ptr in A class. I think I misunderstood a concept a bit again. I wanted to behave it in this way: if unique_ptr dies, the object itself dies too, even though shared_ptrs still point to it, but that's silly as they wouldn't know that object itself was destroyed and therefore they wouldn't be nullptrs.

In that case, you're looking at the wrong smart pointer. What you probably need is called std::weak_ptr. If so, then make sure you use one std::shared_ptr and all others as std::weak_ptr.

When shared_ptr, when unique_ptr

shared_ptr solves memory management problems the way regular expressions solve html parsing problems.

shared_ptr can be part of a solution for a lifetime management issue, but it is in no way, shape or form something to be used casually. It is extremely easy to have "misplaced" pointers, or reference loops, with shared_ptr. In my experience, use shared_ptr as an internal private implementation detail with guards, invariants and axioms that together prove that you cannot form loops, and you have a decent chance of not having problems.

Over half of my use of shared_ptr consists of a single location that "owns" the pointer, and other observers that have weak_ptrs except for narrow windows when they check that the resource is still around, plus reason to think that the shared_ptr won't die in that narrow window.

Another good chunk of use is when I have a copy-on-write situation, where I have a nearly-immutable object state that can be copied around (stored in a shared_ptr<const T> pimpl. When a write operation occurs, if I'm the only user I cast that to a shared_ptr<T> and modify it. Otherwise, I copy it into a shared_ptr<T> and modify it. Then I store it back as the shared_ptr<const T> in both cases.

Simply scattering shared_ptrs around in my experience inevitably leads to leaks and resources lasting far longer than they should.


On the other hand, you should just use unique_ptr. make_unique and unique_ptr should replace new and delete in your code in nearly every circumstance. It is really, really hard to get unique_ptr wrong, and when you do, it is usually because the old code had a serious leak risk, or you didn't understand how the resource was being managed before.

In new code, it is a no-brainer. In old code, if you need to understand the lifetime of the objects involved, you will have to learn enough to put the ownership in a unique_ptr probably anyhow. The big exception is when you are doing "cargo cult" programming (modifying a complex system you don't understand in ways that look like the other code in the system, and hoping that it will work because the other code worked) is not feasible to manage resource that way. There are also some other exceptions, like objects who manage their own lifetime in a complex way.

Managing non-new'd resources with unique_ptr is a touch harder, but I also find worth it.

And sometimes you are forced to .release the pointer into a long C-style void* filled call-chain.


There is also a run-time overhead on shared_ptr, but the conceptual overhead of shared_ptr making object lifetime much harder to understand is the real reason to avoid using it.

shared_ptr can be used to do fancy things, but it does not solve your problem, it is a tool that as part of a comprehensive resource management system you can use to make your solution a bit simpler.

There is nearly zero run-time overhead on unique_ptr: it is the same size as a pointer, and it simply calls delete at end of lifetime.

unique_ptr solves entire resource management problems dead in their tracks. They evaporate once you use it properly.


In your specific example, I'd either put unique_ptrs in tree, and keep raw pointers in nodes.

Alternatively, keep unique_ptrs in the children vector, and raw pointers in the tree and in the parent pointer.

In either case, all operations that add/remove nodes should go through tree (taking a node argument for the target), as the state of tree and the nodes needs to be kept in sync.


You expressed an interest in getting a random node when I pointed out that the list of nodes in the root tree was a bad idea.

Simply store the number of children in each node. (This requires work when you add/modify/delete children that must cascade up to the root).

Add:

node* node::nth_node( int n ) {
if (n == 0) return this;
--n;
for( auto&& child:children ) {
if (n < child->subtree_size)
return child->nth_node(n);
n -= child->subtree_size;
}
return nullptr; // n is too big
}

this gets the nth descendent of a node, assuming subtree_size is how big a tree node is a root of (including itself, so it should never be 0).

To get a random node from tree, create a random number from 0 to root->subtree_size. Ie, if root->subtree_size is 3, your random number is 0, 1 or 2.

Then call root->nth_node( that_random_number ).



Related Topics



Leave a reply



Submit