为什么shared_ptr
有allocate_shared而unique_ptr
没有allocate_unique?我想使用自己的分配器创建一个unique_ptr:我是否必须自己分配缓冲区然后将其分配给unique_ptr?
这似乎是一个明显的习语。
答案 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;
}