Why does unique_ptr<Derived> implicitly cast to unique_ptr<Base>?
The bit of magic you're looking for is the converting constructor #6 here:
template<class U, class E>
unique_ptr(unique_ptr<U, E> &&u) noexcept;
It enables constructing a std::unique_ptr<T>
implicitly from an expiring std::unique_ptr<U>
if (glossing over deleters for clarity):
unique_ptr<U, E>::pointer
is implicitly convertible topointer
Which is to say, it mimicks implicit raw pointer conversions, including derived-to-base conversions, and does what you expect™ safely (in terms of lifetime – you still need to ensure that the base type can be deleted polymorphically).
Because std::unique_ptr
has a converting constructor as
template< class U, class E > unique_ptr( unique_ptr<U, E>&& u ) noexcept;
and
This constructor only participates in overload resolution if all of the following is true:
a)
unique_ptr<U, E>::pointer
is implicitly convertible topointer
...
A Derived*
could convert to Base*
implicitly, then the converting constructor could be applied for this case. Then a std::unique_ptr<Base>
could be converted from a std::unique_ptr<Derived>
implicitly just as the raw pointer does. (Note that the std::unique_ptr<Derived>
has to be an rvalue for constructing std::unique_ptr<Base>
because of the characteristic of std::unique_ptr
.)
You can implicitly construct a std::unique_ptr<T>
instance from an rvalue of std::unique_ptr<S>
whenever S
is convertible to T
. This is due to constructor #6 here. Ownership is transferred in this case.
In your example, you have only rvalues of type std::uinque_ptr<Derived>
(because the return value of std::make_unique
is an rvalue), and when you use that as a std::unique_ptr<Base>
, the constructor mentioned above is invoked. The std::unique_ptr<Derived>
objects in question hence only live for a short amount of time, i.e. they are created, then ownership is passed to the std::unique_ptr<Base>
object that is used further on.