Почему shared_ptr
имеет allocate_shared, а unique_ptr
не имеет allocate_unique?
Я хотел бы сделать unique_ptr с помощью своего собственного распределителя: нужно ли мне самому распределить буфер и назначить его уникальному_ptr?
Это кажется очевидной идиомой.
Почему в С++ 14 нет функции std:: allocate_unique?
Ответ 1
Почему
shared_ptr
имеетallocate_shared
, аunique_ptr
не имеетallocate_unique
?
shared_ptr
нуждается в нем, чтобы он мог выделять свое внутреннее общее состояние (счетчик ссылок и удаление), а также общий объект, используя распределитель. unique_ptr
управляет только объектом; поэтому нет необходимости предоставлять распределитель самому unique_ptr
и меньше необходимости в функции allocate
.
(По той же причине также меньше потребности в make_unique
, вероятно, поэтому она не присутствовала в С++ 11, но была добавлена в С++ 14 по большому спросу ради согласованности. Возможно, тот же спрос добавит allocate_unique
к будущему стандарту.)
Я должен сам выделить буфер и назначить его уникальному_ptr?
Да. Или вы можете написать свой собственный allocate_unique
; в отличие от allocate_shared
, возможно и достаточно просто реализовать его отдельно от unique_ptr
. (Как уже упоминалось в комментариях, вам нужно убедиться, что он использовал соответствующий деаэратор для распределителя, дефолт по умолчанию использовал бы delete
и пошел бы ужасно неправильно).
Это кажется очевидной идиомой.
Действительно. Но так много других идиом, и не все может (или должно) быть стандартизованным.
Для более официального обоснования нынешнего отсутствия allocate_unique
, см. предложение для make_unique
, в частности раздел 4 (Custom Deleters).
Ответ 2
Я должен сам выделить буфер, а затем назначить его уникальному_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;
}