简体   繁体   中英

assymetry between std::shared_ptr and std::unique_ptr constructors

During C++03 we did not have unique_ptr , so I had used the tr1::shared_ptr instead.

Now, in C++11, in such cases, I am replacing calls like this:

tr1::shared_ptr<X> o(new X); 

with

std::unique_ptr<X> o(new X);

Unfortunately, for some reason, I cannot replace cases containg a deleter, where the deleter is a function:

void special_delete(Y *p) { ... }

tr1::shared_ptr<Y> o(new Y(), special_delete); 


std::unique_ptr<Y> o(new Y(), special_delete);  // does not compile
std::unique_ptr<Y, std::function<void(Y*)> > o(new Y(), special_delete);  // this compiles

Why does this happen? Is there a homogeneous way I can replace all shared_ptr constructors with unique_ptr constructors ?

I have created this , but I am not really happy about it..

template <class Y>
using unique_ptr_with_deleter = std::unique_ptr<Y, std::function<void(Y*)> >;

unique_ptr_with_deleter<Y> o(new Y(), special_delete);  // this compiles

The shared_ptr template type-erases the deleter. There's only one single type shared_ptr<T> for any kind of deleter you want. It's a very heavy-weight (and expensive) tool.

By contrast, the unique_ptr makes the deleter part of the type, and so it has no dynamic call overhead. A unique_ptr<T, D> is very nearly as cheap as a raw pointer.

Note that you can construct a shared_ptr<T> from a unique_ptr<T, D> rvalue for any deleter type D .


Random commentary on your solutions: neither solution is great. Using the function pointer as the deleter needlessly makes the deleter a dynamic part of your unique_ptr state. The std::function case is even worse, using an entire type-erasing virtual dispatch mechanism for the deletion -- keeping in mind that you know statically how to delete!

The better solution is to make the deleter part of the type , not part of the value, by writing your own type:

struct YDeleter { void operator()(Y* p) { special_delete(p); } };

std::unique_ptr<Y, YDeleter> p(new Y);   // or presumaby some special factory

That way, the entire deletion logic is known at compile time and can be inlined as much as possible, without additional function call indirection.

You should supply deleter type

std::unique_ptr<Y, decltype(&special_delete)> o{new Y{}, &special_delete};

Note that replacing tr1::shared_ptr<X> o(new X); with std::unique_ptr<X> o(new X); is not a good idea and is not supposed to work in general. std::unique_ptr is more like a replacement for auto_ptr .

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