【问题标题】:Why is there no std::allocate_unique function in C++14?为什么 C++14 中没有 std::allocate_unique 函数?
【发布时间】:2014-04-17 10:29:40
【问题描述】:

为什么shared_ptr 有 allocate_shared 而unique_ptr 没有 allocate_unique?
我想使用我自己的分配器创建一个 unique_ptr:我是否必须自己分配缓冲区,然后将其分配给一个 unique_ptr?
这似乎是一个明显的习语。

【问题讨论】:

    标签: c++ std c++14


    【解决方案1】:

    我是否必须自己分配缓冲区,然后将其分配给 unique_ptr?

    不仅仅是一个缓冲区,一个指向对象的指针。但是对象可能需要被分配器销毁,而内存肯定需要被分配器释放,因此您还需要将分配器传递给unique_ptr。它不知道如何使用分配器,因此您需要将其包装在自定义删除器中,这将成为unique_ptr 类型的一部分。

    我认为一个通用的解决方案应该是这样的:

    #include <memory>
    
    template<typename Alloc>
    struct alloc_deleter
    {
      alloc_deleter(const Alloc& a) : a(a) { }
    
      typedef typename std::allocator_traits<Alloc>::pointer pointer;
    
      void operator()(pointer p) const
      {
        Alloc aa(a);
        std::allocator_traits<Alloc>::destroy(aa, std::addressof(*p));
        std::allocator_traits<Alloc>::deallocate(aa, p, 1);
      }
    
    private:
      Alloc a;
    };
    
    template<typename T, typename Alloc, typename... Args>
    auto
    allocate_unique(const Alloc& alloc, Args&&... args)
    {
      using AT = std::allocator_traits<Alloc>;
      static_assert(std::is_same<typename AT::value_type, std::remove_cv_t<T>>{}(),
                    "Allocator has the wrong value_type");
    
      Alloc a(alloc);
      auto p = AT::allocate(a, 1);
      try {
        AT::construct(a, std::addressof(*p), std::forward<Args>(args)...);
        using D = alloc_deleter<Alloc>;
        return std::unique_ptr<T, D>(p, D(a));
      }
      catch (...)
      {
        AT::deallocate(a, p, 1);
        throw;
      }
    }
    
    int main()
    {
      std::allocator<int> a;
      auto p = allocate_unique<int>(a, 0);
      return *p;
    }
    

    【讨论】:

    • @LucDanton,我回滚了你的编辑,我选择拒绝而不是非常明确地重新绑定。 allocate_shared 无论如何都要重新绑定,所以没关系,但这里不是这样。如果容器可以需要正确的 value_type,那么我的 allocate_unique impl 也可以:-)
    • 谢谢乔纳森。最近我一直在思考我的 ACCU 笔记而不是编码,但是我在三个地方都使用了这个代码,没有发生任何事故,所以它暂时获得了 Hatcat 的认可。
    • @hatcat,很酷,很高兴它正在工作。感谢您在 ACCU 的闪电般的演讲,它提醒了我是多么想玩 Isolation。
    • +1 -- 我自己也写了同样的东西(一直到main 代码)以了解与D::pointer 的交易。以为我应该发布它,但这里已经是 :-) 在我的版本中,我从模板参数中删除了 T 并改用了 AT::value_type,而且我没有私有成员,而是使用基类。
    • 我使用了类似的方法,只是为了支持多态类,我将验证更改为接受std::is_same&lt;&gt;std::is_base_of&lt;&gt;。通过这个更改,我可以为派生类创建对象,同时仍然使用基类参数化的分配器。
    【解决方案2】:

    为什么shared_ptrallocate_sharedunique_ptr 没有allocate_unique

    shared_ptr 需要它,以便它可以使用分配器分配其内部共享状态(引用计数和删除器)以及共享对象。 unique_ptr 只管理对象;因此无需为unique_ptr 本身提供分配器,并且更少需要allocate 函数。

    (出于同样的原因,对make_unique 的需求也较少,这可能就是为什么它没有在 C++11 中提供,而是出于大众需求而被添加到 C++14 中的原因一致性。也许同样的需求会将allocate_unique添加到未来的标准中。)

    我必须自己分配缓冲区,然后将其分配给 unique_ptr 吗?

    是的。或者你可以自己写allocate_unique;与allocate_shared 不同,它可以与unique_ptr 本身分开实现,而且相当简单。 (如 cmets 中所述,您必须确保它为分配器使用了适当的删除器;默认删除器将使用 delete 并且会出错)。

    这似乎是一个明显的习语。

    确实如此。但许多其他习语也是如此,并不是所有的东西都可以(或应该)标准化。

    有关当前缺少 allocate_unique 的更正式的理由,请参阅 proposal for make_unique,特别是第 4 节(自定义删除器)。

    【讨论】:

    • +1。提案中提到的类型擦除方面是最重要的恕我直言,allocate_unique 返回什么类型?
    • @JonathanWakely:好点子。如果我真的尝试这样做,也许我声称它“相当简单”的说法可能会改变。
    猜你喜欢
    • 2013-09-14
    • 2014-10-09
    • 1970-01-01
    • 2023-02-21
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多