简体   繁体   中英

Why does unique_ptr take two template parameters when shared_ptr only takes one?

Both unique_ptr and shared_ptr accept a custom destructor to call on the object they own. But in the case of unique_ptr , the destructor is passed as a template parameter of the class , whereas the type of shared_ptr 's custom destructor is to be specified as a template parameter of the constructor .

template <class T, class D = default_delete<T>> 
class unique_ptr
{
    unique_ptr(T*, D&); //simplified
    ...
};

and

template<class T>
class shared_ptr
{
    template<typename D>
    shared_ptr(T*, D); //simplified
    ...
};

I can't see why such difference. What requires that?

If you provide the deleter as template argument (as in unique_ptr ) it is part of the type and you don't need to store anything additional in the objects of this type. If deleter is passed as constructor's argument (as in shared_ptr ) you need to store it in the object. This is the cost of additional flexibility, since you can use different deleters for the objects of the same type.

I guess this is the reason: unique_ptr is supposed to be very lightweight object with zero overhead. Storing deleters with each unique_ptr could double their size. Because of that people would use good old raw pointers instead, which would be wrong.

On the other hand, shared_ptr is not that lightweight, since it needs to store reference count, so storing a custom deleter too looks like good trade off.

Shared pointers of different types can share the ownership of the same object . See overload (8) of std::shared_ptr::shared_ptr . Unique pointers don't need such a mechanism, as they don't share .

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

If you didn't type-erase the deleter, you wouldn't be able to use such a shared_ptr<T, Y_Deleter> as a shared_ptr<T> , which would make it basically useless.

Why would you want such an overload?

Consider

struct Member {};
struct Container { Member member };

If you want to keep the Container alive, while you use the Member , you can do

std::shared_ptr<Container> pContainer = /* something */
std::shared_ptr<Member> pMember(pContainer, &pContainer->member);

and only have to hold onto pMember (perhaps put it into a std::vector<std::shared_ptr<Member>> )

Or alternatively, using overload (9)

template< class Y > 
shared_ptr( const shared_ptr<Y>& r ) noexcept; 
  // Only exists if Y* is implicitly convertible to T*

You can have polymorphic sharing

struct Base {};
struct Derived : Base {};

void operate_on_base(std::shared_ptr<Base>);

std::shared_ptr<Derived> pDerived = /* something*/
operate_on_base(pDerived);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM