如何将unique_ptr与更通用的删除器一起使用?

时间:2013-08-23 09:50:11

标签: c++ c++11 unique-ptr

考虑一些功能:

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

因为unique_ptrdefault_delete命令默认模板参数D,所以传递给foo的任何函数对象返回带有非unique_ptr的{​​{1}}默认删除器无法编译。例如,

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>的实例作为默认删除工具?

2 个答案:

答案 0 :(得分:4)

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

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

现在,根据您的评论,我们知道这不是您想要的全部,而是您 希望能够将unique_ptr存储在您的班级中以便使用 它以后。这会产生一系列完全不同的问题:

  1. unique_ptr<T, D1>unique_ptr<T, D2>是不同的类型。因此,我们需要知道您的仿函数unique_ptr<T, D>
  2. 将返回F
  3. 即使我们事先知道F的返回类型,我们的班级仍然只能存储unique_ptr<T, D1>而不是unique_ptr<T, D2>
  4. 最简单的方法(我能想到的,可能会更好 方法)是类型擦除

    我们自己创建一个基类,公开由...管理的指针 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_;
    };
    

    在您真正关心的课程中,您现在可以存储指向我们的指针 基类和使用多态来访问底层对象,在此 案例unique_ptr。假设我们将上面的类移到了 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_;
    };
    

    您可以找到一个完整的示例here

答案 1 :(得分:2)

  

但我想知道为什么这不能合并到unique_ptr的接口中?

因为这样做会迫使所有std::function的开销进入所有人unique_ptr旨在用于指针的单个所有权的任何情况。你支付你使用的费用;并非所有使用自定义删除程序的人都需要将删除程序设为通用。这样,他们就不用付钱了。

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

如果要提供此通用删除器构造,可以创建一个(私有地)从unique_ptr继承并复制其接口的类,减去不带删除器实例的构造函数。这样,用户就被迫传递了删除函数。