The std::unique_ptr and std::shared_ptr are smart-pointers. An std::unique_ptr owns an object exclusively, whereas the ownership of an object can be shared via std::shared_ptr instances.
One of the helpful features of unique_ptr is that it can be seamlessly converted to a compatible shared_ptr. The conversion is possible through an std::shared_ptr<T>'s constructor that takes an rvalue reference of std::unique_ptr<Y, Deleter> type and moves it:
//shared_ptr constructor that takes a unique_ptr&&
template<typename Y, typename Deleter>
shared_ptr(std::unique_ptr<Y,Deleter>&& u);
The Y* must be compatible with T* for the conversion to be possible. The unique_ptr (u) is moved and does not own the object after the conversion.
Below are some trivial examples of converting a unique_ptr to a shared_ptr. Note that a shared_ptr is constructed below through a temporary rvalue or by explicitly moving an lvalue:
//'foo()' returns a unique_ptr
std::unique_ptr<std::string> foo();
//from a function result. Temporary is moved.
std::shared_ptr<std::string> sp1 = foo(); // OK
//A unique_ptr<std::string> initialized
auto up = std::make_unique<std::string>("Hello World");
// Move an lvalue unique_ptr to shared_ptr
std::shared_ptr<std::string> sp2 = std::move(up); // OK
One practical use of this feature is in the factory function pattern, which often produces an instance and returns a pointer to it. A factory method implementation cannot assume whether the ownership of the returned object would be shared, or it would be owned exclusively. One way is to return a raw pointer and let the callers of the factory function wrap the raw pointer in a shared_ptr or a unique_ptr. But the drawback of that approach is that we have to renounce the std::make_unique (or std::make_shared). Moreover, the use of a raw pointer inside a factory method is unsafe.
A more flexible design is to return a unique_ptr from a factory method, which can be used as-is or can be converted to a compatible shared_ptr by the callers.
Consider a polymorphic class hierarchy, as shown below:
struct Base {
// ...
virtual ~Base() { }
};
struct Derived1 : public Base {
// ...
~Derived1() { }
};
struct Derived2 : public Base {
// ...
~Derived2() { }
};
A factory function creates an object of type Derived1 or Derived2 depending on the parameter t and returns an std::unique_ptr<Base>:
std::unique_ptr<Base> factory(int t) {
switch(t) {
case 1:
return std::make_unique<Derived1>();
case 2:
return std::make_unique<Derived2>();
default:
return nullptr;
}
}
Below are a few cases of converting the result of the factory function to an std::shared_ptr:
A
std::shared_ptr<Base> ptrBase = factory(1);
B
std::shared_ptr<Derived2> ptrDerived2 = factory(2);
C
std::shared_ptr<void> ptrVoid = factory(1);
One or more of the above cases are invalid (would not compile) because of the use of incompatible std::shared_ptr.
Select below all the valid cases from above (check Explanations
for details):
The flawless conversion of an std::unique_ptr to a compatible std::shared_ptr makes it possible to write efficient and safe factory functions. However, note that an std::shared_ptr cannot be converted to an std::unique_ptr.
Constructors of std::shared_ptr - cppreference
The std::shared_ptr<void> as arbitrary user-data pointer - nextptr