0

考虑一些功能:

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

因为unique_ptr规定了默认模板参数 , default_deletefor D,所以任何传递给foo返回 aunique_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>

4

2 回答 2

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>你的函子将返回什么F
  2. 即使我们F事先知道返回类型,我们的类仍然只能存储unique_ptr<T, D1>而不是unique_ptr<T, D2>

解决这个问题的最简单方法(我能想到,可能有更好的方法)是type erasure

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

您可以在此处找到一个完整的示例。

于 2013-08-23T10:12:00.460 回答
2

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

因为这样做会迫使所有人承担所有std::function开销。旨在用于几乎任何指针单一所有权的情况。你为你使用的东西付费;不是每个使用自定义删除器的人都需要该删除器是通用的。这样,他们就不必为此付费。unique_ptr

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

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

于 2013-08-23T10:11:53.267 回答