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 to pointer

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 to pointer

...

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.