Я пытаюсь использовать boost::pool_allocator для (эффективного) распределения std::shared_ptr<T>. Концептуально я хочу что-то вроде этого:
struct Foo {};
std::shared_ptr<Foo> create() {
static boost::pool_alloator<Foo> alloc;
return std::allocate_shared<Foo>(alloc);
}
В этом примере есть одна проблема: boost::pool_allocator<Foo> оптимизирован для выделения размера sizeof(Foo) (на самом деле я удивлен, что он вообще поддерживает выделения разных размеров…). Но std::allocate_shared<Foo>(…) не выделяет sizeof(Foo) байты, поскольку он выделяет управляющий блок и полезную нагрузку за один раз.
Чтобы эффективно использовать boost::pool_allocator<…> с std::allocate_shared, мне нужно будет сообщить pool_allocator размер ожидаемых выделений. Для этого мне, вероятно, понадобится тип, который allocate_shared выделяет внутреннее пространство. Я почти уверен, что для реализации STL GCC этим типом будет _Sp_counted_ptr_inplace , а для libc++ Clang это выглядит как __shared_ptr_emplace::_Storage - но использование этих типов явно не будет переносимым.
Существует ли какой-либо стандартный способ определения размера выделений, которые std::allocate_shared выполнит? Или какой-нибудь другой способ (эффективно) использовать распределитель пула для allocate_shared?
Возможно, это связано Пользовательский распределитель (пул) с наддувомshared_ptr
std lib должен использовать allocator_traits::rebind_alloc и allocator_traits::rebind_traits внутри (применяется правило asif). Таким образом, распределитель должен иметь хорошо себя rebind член шаблонного типа.





Распределитель привязывается к внутреннему типу того, что он выделяет, и вместо этого используется копия распределителя соответствующего типа.
pool_allocator<Foo> немного вводит в заблуждение, поскольку он привязан к более крупному типу. Обычно вы используете allocator<void>, когда знаете, что произойдет отскок:
std::shared_ptr<Foo> create() {
static boost::pool_allocator<void> alloc;
return std::allocate_shared<Foo>(alloc);
}
Таким образом, вы точно знаете, что для обработки распределителей для объекта меньшего размера, который никогда не используется, не требуется дополнительной работы.
Ах, это приятно знать, я не знал об этой логике перепривязки. Если я правильно вас понял, при перепривязке создается новый объект pool_allocator. Каково время жизни этого объекта в этом примере? этот распределитель отскока создается (и уничтожается) для каждого вызова create()? Это означало бы, что «пул» по сути бесполезен…
@LukasBarth Да, я так думаю: копия распределителя создается для выделения блока управленияshared_ptr +, а другая копия сохраняется в блоке управления для последующего удаления. Распределители с сохранением состояния обычно представляют собой просто указатель на что-то другое (например, local_pool p; pool_allocator<T>(&p)), поэтому копии обходятся дешево. В случае pool_allocator я думаю, что это указатель на синглтон.
Да, я только что снова прочитал документацию boost::pool_allocator — действительно, за кулисами есть одноэлементный пул, так что все должно быть хорошо (но я также могу избавиться от статической переменной). Большое спасибо!
Я не удивлюсь, если нет способа получить эту информацию, обратите внимание, что
std::allocate_sharedна самом деле не обязательно использовать одно распределение en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared