【发布时间】:2014-04-17 10:29:40
【问题描述】:
为什么shared_ptr 有 allocate_shared 而unique_ptr 没有 allocate_unique?
我想使用我自己的分配器创建一个 unique_ptr:我是否必须自己分配缓冲区,然后将其分配给一个 unique_ptr?
这似乎是一个明显的习语。
【问题讨论】:
为什么shared_ptr 有 allocate_shared 而unique_ptr 没有 allocate_unique?
我想使用我自己的分配器创建一个 unique_ptr:我是否必须自己分配缓冲区,然后将其分配给一个 unique_ptr?
这似乎是一个明显的习语。
【问题讨论】:
我是否必须自己分配缓冲区,然后将其分配给 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;
}
【讨论】:
allocate_shared 无论如何都要重新绑定,所以没关系,但这里不是这样。如果容器可以需要正确的 value_type,那么我的 allocate_unique impl 也可以:-)
main 代码)以了解与D::pointer 的交易。以为我应该发布它,但这里已经是 :-) 在我的版本中,我从模板参数中删除了 T 并改用了 AT::value_type,而且我没有私有成员,而是使用基类。
std::is_same<> 或std::is_base_of<>。通过这个更改,我可以为派生类创建对象,同时仍然使用基类参数化的分配器。
为什么
shared_ptr有allocate_shared而unique_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 节(自定义删除器)。
【讨论】:
allocate_unique 返回什么类型?