如何将unique_ptr与更通用的deleter一起使用

How can I use unique_ptr with a more generic deleter?

本文关键字:deleter 一起 unique ptr      更新时间:2023-10-16

考虑一些函数:

template<typename F>
void foo(F f) {
  std::unique_ptr<int> p = f();
  // do some stuff with p
}

因为unique_ptrD指定了默认模板参数default_delete,所以传递给foo的任何函数对象如果返回具有非默认deleter的unique_ptr,都无法编译。例如,

int x = 3;
foo([&x](){
    // use empty deleter
    return std::unique_ptr<int>(&x, [](int*){});
});

然而,我认为这可能是有用的,我看不出它不可能实现的直接原因。有没有一种共同的方法来解决这个问题?

编辑

简单的解决方案是定义foo,而不是使用以下内容:

  std::unique_ptr<int, std::function<void(int*)>> p = f();

但我想知道为什么这不能被纳入unique_ptr的接口中?类接口不能提供这个泛型属性是有原因的吗?有没有办法将这种东西"包装"成一个新的定义?

例如,

template<typename T>
using Generic_unique_ptr =
  std::unique_ptr<
    T,
    std::function< void(typename std::unique_ptr<T>::element_type*) >
  >;

但这似乎很危险,因为它暴露了做以下事情的可能性,

Generic_unique_ptr<int> p(new int());

这将使删除程序未初始化并且表现出未定义的行为。也许有什么方法可以为提供std::default_delete<T>的实例作为默认删除程序?

如果你只想在函数中使用指针,你可以使用auto关键字;编译器将推导出unique_ptr的类型它已经被使用,因此自动做正确的事情:

template <typename F>
void foo(F f)
{
    auto p = f();
    p->bar();
}

现在,从你的评论中,我们知道这不是你想要的全部,但你希望能够将unique_ptr存储在您的类中以便使用稍后。这就产生了一系列完全不同的问题:

  1. CCD_ 12和CCD_。因此,我们需要知道您的函子F将返回什么unique_ptr<T, D>
  2. 即使我们事先知道F的返回类型,我们的类仍然只能存储unique_ptr<T, D1>而不能存储unique_ptr<T, D2>

我能想到的最简单的方法可能是ways)是类型的擦除

我们为自己创建了一个基类,该基类公开由unique_ptr:

template <typename T>
struct wrapper
{
    virtual ~wrapper() {}
    virtual T const * get() const = 0;
    virtual T * get() = 0;
};

从该类继承我们实际的存储类,该存储类推导类型unique_ptr:

template <typename T, typename F>
struct storage
    : wrapper<T>
{
    storage(F f) { p_ = f(); }
    T const * get() const { return p_.get(); }
    T * get() { return p_.get(); }
    private:
        typename std::result_of<F()>::type p_;
};

在您真正关心的类中,您现在可以存储指向基类,并使用多态性来访问底层对象将CCD_ 21。假设我们将上面的类移动到namespace detail对用户隐藏它们:

template <typename T>
class some_class
{
    public:
        template <typename F>
        void store(F f)
        {
            storage_.reset(new detail::storage<T, F>(f));
        }
        T const * get() const { return storage_->get(); }
        T * get() { return storage_->get(); }
    private:
        std::unique_ptr<detail::wrapper<T>> storage_;
};

你可以在这里找到一个完全有效的例子。

但我想知道为什么这不能被纳入unique_ptr的接口中?

因为这样做会迫使std::function的所有开销都转移到每个上。unique_ptr旨在对指针的单一所有权的几乎任何情况都很有用。你为你使用的东西付费;并不是每个使用自定义deleter的人都需要该deleter是泛型。这样,他们就不必为此付费。

此外,当前的方法允许它处理非指针资源,因为deleter可以准确地指定unique_ptr中存储的类型。

如果您想提供这种通用的deleter构造,您可以创建一个类,该类(私下)继承自unique_ptr并复制其接口,减去不使用deleter实例的构造函数。这样,用户就被迫在.

中传递一个deleter函数