Search code examples
c++c++20unique-ptr

Deleters for unique_ptr


There is an array holding unique pointers:

std::array<std::unique_ptr<T, deleter<allocator<T>>>> storage;

where

template<typename ALLOC>
class deleter {
    void operator()( void ) { ... }
};

does the deletion as required by unique_ptr. Effectively, it calls the destructor and then deallocates the memory occupied. So far so good.

But there is another deleter:

template<typename T>
class empty_deleter {
    void operator()( void ) {}
};

which performs no operation at all - no destruction, no deallocation.

The reason why it exists is to have, in theory, the option to store object owned by such unique_ptr<T, empty_deleter<T>> within the storage array...

The question is - how to achieve that? To make the deleters compatible so that I can assign unique_ptr<T, empty_deleter<T>> to an array of unique_ptr<T, deleter<allocator<T>>> pointers...

I know there are converting constructors within the unique_ptr implementation so in theory, the deleter of one type can be assinged to the unique_ptr being declared with another type but there is a constraint these deleters shall be convertible somehow... could you please advice me how to reach that?

Additionally - once I am successfull in assigning the deleter instance of empty_deleter<T> into unique_ptr<T, deleter<allocator<T>>> whatever how, which operator() is going to be called once the deletion is triggered? The one from empty_deleter<T> or from deleter<allocator<T>>?


Solution

  • As @Nicol Bolas points out, "object owned by such unique_ptr<T, empty_deleter<T>>" is nonsensical. I will answer "how to make a smart pointer that sometimes owns and sometimes doesn't own it's pointee".

    None of std::unique_ptr<T, empty_deleter<T>>, std::unique_ptr<T, deleter<allocator<T>>>, nor std::unique_ptr<T, deleter<other_allocator<T>>> are assignable to one another.

    If you want to mix and match ownerships of your pointers, you will have to type-erase the deleter. The simplest way is to use the existing function-object type-erasure type, std::function.

    template <typename T>
    class pmr_unique_ptr : public std::unique_ptr<T, std::function<void(T *)>> {
    public:
        using unique_ptr::unique_ptr;
      // have to supply a deleter
        pmr_unique_ptr(pointer) = delete;
        pmr_unique_ptr() = delete;
        pmr_unique_ptr(std::nullptr_t) = delete;
    };
    

    This can be constructed from std::unique_ptr<T, D> so long as D is copyable.