Search code examples
c++memory-managementc++17smart-pointersunique-ptr

How can I safely overload custom deleter of std::unique_ptr?


I am trying to reduce code duplication while using std::unique_ptr and it's custom deleter.

I have the some containers Foo, Bar which are allocated using one custom allocator, so cannot be freed with delete.

So the current code is:

struct UniqueFooDeleter
{
   void operator()(Foo* foo) 
   {
      internal_free(foo);
   }
};
using unique_foo_ptr = std::unique_ptr<Foo, UniqueFooDeleter>;

struct UniqueBarDeleter 
{
   void operator()(Bar* bar) 
   {
      internal_free(bar);
   }
};
using unique_bar_ptr = std::unique_ptr<Bar, UniqueBarDeleter>;

I changed it to:

struct UniqueInternalDeleter
{
   void operator()(Bar* bar)
   {
      internal_free(bar);
   }

   void operator()(Foo* foo)
   {
      internal_free(foo);
   }
};
using unique_bar_ptr = std::unique_ptr<Bar, UniqueInternalDeleter>;
using unique_foo_ptr = std::unique_ptr<Foo, UniqueInternalDeleter>;

How can I do better such that any number of containers that are allocated via internal_free can be used as std::unique_ptrs ?


Solution

  • You can make the UniqueInternalDeleter as templated functor and static_assert, if the T is not Foo or bar.

    #include <type_traits> // std::is_same_v
    
    template<typename T>
    struct UniqueInternalDeleter /* final */
    {
       static_assert(std::is_same_v<T, Foo> || std::is_same_v<T, Bar>,
          " T must be either Foo or Bar");
    
       void operator()(T* barOrfoo)
       {
          internal_free(barOrfoo);
       }
    
    private:
       void internal_free(T* barOrfoo)
       {
          if constexpr(std::is_same_v<T, Foo>)
             // code for `Foo*`
          else
             // code for `Bar*`
    
       }
    };
    

    This makes the your alias to be more specific for Bar and Foo:

    using unique_bar_ptr = std::unique_ptr<Bar, UniqueInternalDeleter<Bar>>;
    using unique_foo_ptr = std::unique_ptr<Foo, UniqueInternalDeleter<Foo>>;