C++20: совместимый со стандартами способ получения размера выделения, выполняемого std::allocate_shared

Я пытаюсь использовать 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?

Я не удивлюсь, если нет способа получить эту информацию, обратите внимание, что std::allocate_shared на самом деле не обязательно использовать одно распределение en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared

Alan Birtles 07.05.2024 16:09

Возможно, это связано Пользовательский распределитель (пул) с наддувомshared_ptr

Pepijn Kramer 07.05.2024 16:16

std lib должен использовать allocator_traits::rebind_alloc и allocator_traits::rebind_traits внутри (применяется правило asif). Таким образом, распределитель должен иметь хорошо себя rebind член шаблонного типа.

Red.Wave 07.05.2024 16:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
3
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Распределитель привязывается к внутреннему типу того, что он выделяет, и вместо этого используется копия распределителя соответствующего типа.

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()? Это означало бы, что «пул» по сути бесполезен…

Lukas Barth 07.05.2024 22:05

@LukasBarth Да, я так думаю: копия распределителя создается для выделения блока управленияshared_ptr +, а другая копия сохраняется в блоке управления для последующего удаления. Распределители с сохранением состояния обычно представляют собой просто указатель на что-то другое (например, local_pool p; pool_allocator<T>(&p)), поэтому копии обходятся дешево. В случае pool_allocator я думаю, что это указатель на синглтон.

Artyer 07.05.2024 22:12

Да, я только что снова прочитал документацию boost::pool_allocator — действительно, за кулисами есть одноэлементный пул, так что все должно быть хорошо (но я также могу избавиться от статической переменной). Большое спасибо!

Lukas Barth 07.05.2024 22:14

Другие вопросы по теме