为什么C ++ 14中没有std :: allocate_unique函数?

时间:2014-04-17 10:29:41

标签: c++ std c++14

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

2 个答案:

答案 0 :(得分:14)

  

shared_ptr为什么allocate_shared unique_ptr allocate_unique没有shared_ptr

unique_ptr需要它,以便它可以使用分配器分配其内部共享状态(引用计数和删除器)以及共享对象。 unique_ptr只管理对象;所以不需要为allocate本身提供分配器,也不需要make_unique函数。

(由于同样的原因,对allocate_unique的需求也较少,这可能是它在C ++ 11中没有特色的原因,但是由于受欢迎的需求而被添加到C ++ 14中。为了保持一致性,也许相同的需求会将allocate_unique添加到未来的标准中。)

  

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

是。或者你可以写自己的allocate_shared;与unique_ptr不同,与delete本身分开实现它是可能的,而且相当简单。 (正如评论中所提到的,你必须确保它为分配器使用了一个合适的删除器;默认删除器将使用allocate_unique并且出现可怕的错误。)

  

这似乎是一个显而易见的习语。

事实上。但是许多其他习语也是如此,并非所有事情都可以(或应该)标准化。

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

答案 1 :(得分:13)

  

我是否必须自己分配缓冲区然后将其分配给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;
}