简体   繁体   中英

std::unique_ptr, custom deleter and type change

I would like to use unique_ptr with my deleter. I would like my unique_ptr with my deleter to be fully compatible with unique_ptr with default deleter.

I did so:

template <typename T>
struct QObjectDeleteLaterDeletor :
        public std::default_delete<T>
{
    void operator()(T *p)
    {
        p->deleteLater();
    }
};

template <typename T, class... Args>
std::unique_ptr<T> qtMakeUniqueSpecial(Args&&... args)
{
    return std::unique_ptr<T>(
                new T(std::forward<Args>(args)...),
                QObjectDeleteLaterDeletor<T>());
}

This compiles, but does not work. My custom deleter ignored and default one used instead as if I did not specify it at all.

I need all of this to be possible to do things like that:

auto ptr1 = qtMakeUniqueSpecial<MyObject>();
std::unique_ptr<MyObject> ptr2;
ptr2 = std::move(ptr1);

Please note that now even ptr1.reset() will lead to calling the standard deleter, not my one.

Is it even possible?

You need specify QObjectDeleteLaterDeletor as the template argument; otherwise, std::default_delete will be used as the deleter, which is slicing-copied from QObjectDeleteLaterDeletor .

template <typename T, class... Args>
std::unique_ptr<T, QObjectDeleteLaterDeletor<T>> qtMakeUniqueSpecial(Args&&... args)
{
    return std::unique_ptr<T, QObjectDeleteLaterDeletor<T>> (
                new T(std::forward<Args>(args)...),
                QObjectDeleteLaterDeletor<T>());
}

Note that you declare ptr2 as std::unique_ptr<MyObject> , then ptr2 will destroy the pointer by std::default_delete . If you declare its type accord with ptr1 like auto ptr2 = std::move(ptr1); then it'll be fine.

You're trying to use

namespace std {
  template<typename T, typename Deleter = default_delete<T> >
  class unique_ptr;
}

which has a second template argument Deleter . Failing to specify that, it defaults to std::default_delete<T> . Your code

std::unique_ptr<T>(new T(std::forward<Args>(args)...),
                   QObjectDeleteLaterDeletor<T>());

passes a const std::default_delete<T>& to the constructor of std::unique_ptr<T> , because that's what the constructor expects. Upon destruction (or call to member reset() ), this will be called.

Note that the behaviour of std::shared_ptr is different: there is no second template argument for the deleter, though a custom deleter may be provided at construction (this will need to be stored via type erasure, which is avoided with std::unique_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