【问题标题】:How can I safely overload custom deleter of std::unique_ptr?如何安全地重载 std::unique_ptr 的自定义删除器?
【发布时间】:2020-08-01 15:12:10
【问题描述】:

我正在尝试在使用 std::unique_ptr 时减少代码重复,它是自定义删除器。

我有一些容器FooBar,它们是使用一个自定义分配器分配的,所以不能用delete释放。

所以现在的代码是:

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>;

我改成:

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>;

我怎样才能做得更好,以便通过internal_free 分配的任意数量的容器都可以用作std::unique_ptrs?

【问题讨论】:

  • 你为什么担心std::unique_ptr&lt;Tri, UniqueInternalDeleter&gt;?一旦你尝试使用这种类型的变量,你会得到一个编译时错误。
  • @Evg 很遗憾我冲过去了。IDE 没有抱怨,所以我没有编译它。让我换个帖子。
  • 为了简化 UniqueInternalDeleter 使 operator() 成为一个接受任何 T 的模板,并让用户负责提供正确的删除器或使用类似于 JeJo 的答案的 static_assert。跨度>
  • 对于您的口渴编辑/问题更新,我能想到的最简单的方法是,您可以提供一个变量模板来检查允许的类型并在提供的答案中使用static_asserttemplate&lt;typename Container&gt; inline constexpr bool hasInternalFreeAllocator = std::is_same_v&lt;T, Foo&gt; || std::is_same_v&lt;T, Bar&gt; || other container....;跨度>

标签: c++ memory-management c++17 smart-pointers unique-ptr


【解决方案1】:

如果T 不是Foobar,您可以将UniqueInternalDeleter 设为模板函子和static_assert

#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*`

   }
};

这使您的别名更具体地用于BarFoo

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

【讨论】:

  • 我建议你把template移到deleter里面,把operator()做一个模板。
  • Jejo:我提出的问题更具体一点:目前的问题是,当有多个容器时,删除器的断言中会出现|| 链。抄送@Evg
  • @JeJo,我猜,没有。但不提类型似乎是一个很大的优势。
  • JeJo, internal_free 接受 void*,因此我的用例不需要 private 中的代码。您可以保留它以供将来的读者阅读。
【解决方案2】:

一种可能的方法:

template<class> struct needs_internal_free : std::false_type { };

struct unique_internal_deleter {
    template<class T>
    void operator()(T* ptr) const {
        static_assert(needs_internal_free<T>::value);
        internal_free(ptr);
    }
};

template<class T>
using unique_internal_ptr = std::unique_ptr<T, unique_internal_deleter>;

现在我们可以声明特定的类型了:

template<> struct needs_internal_free<Foo> : std::true_type { };
using unique_foo_ptr = unique_internal_ptr<Foo>;

template<> struct needs_internal_free<Bar> : std::true_type { };
using unique_bar_ptr = unique_internal_ptr<Bar>;

【讨论】:

  • @JeJo,当然。我只是想将needs_internal_free(或use_...,不确定新名称是否好)与FooBar等解耦,这样就可以通过添加独立的特化来添加新类型。否则,needs_internal_free 看起来是多余的。
猜你喜欢
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-09
  • 2018-01-02
  • 2017-05-15
  • 2018-01-31
  • 2021-11-10
相关资源
最近更新 更多