размеры и виды, гарантии

Я не могу найти доказательства / опровержения того, что следующий фрагмент кода не имеет недостатков дизайна, говоря о правильности.

template <class Item>
class MyDirtyPool {
public:
template<typename ... Args>
std::size_t add(Args &&... args) {

if (!m_deletedComponentsIndexes.empty()) {
//reuse old block
size_t positionIndex = m_deletedComponentsIndexes.back();
m_deletedComponentsIndexes.pop_back();
void *block = static_cast<void *> (&m_memoryBlock[positionIndex]);
new(block) Item(std::forward<Args>(args) ...);

return positionIndex;
} else {
//not found, add new block
m_memoryBlock.emplace_back();

void *block = static_cast<void *> (&m_memoryBlock.back());
new(block) Item(std::forward<Args>(args) ...);

return m_memoryBlock.size() - 1;
}
}
//...all the other methods omitted
private:
struct Chunk {
char mem[sizeof(Item)]; //is this sane?
};

std::vector<Chunk> m_memoryBlock; //and this one too, safe?
std::deque<std::size_t> m_deletedComponentsIndexes;
};

Я беспокоюсь обо всем, что связано с Chunk, который здесь используется в основном как мешок памяти того же размера, что и поставляемый тип.
Я не хочу явно создавать объекты Item в m_memoryBlockпоэтому мне нужен какой-то «кусок памяти с достаточным пространством».

Могу ли я быть уверен, что Chunk будет иметь тот же размер, что и поставляемый тип? Пожалуйста, приведите несколько примеров, когда это предположение не будет работать.

Если я полностью ошибаюсь в своих предположениях, как мне с этим бороться?

3

Решение

В таких проектах память должна быть соответствующим образом выровнена для типа объектов, которые вы хотели бы создать там.

Стандартные встроенные типы обычно имеют натуральный выравнивание, которое равно их sizeofт.е. sizeof(T) == alignof(T),

Выравнивание char массив составляет 1 байт, что недостаточно для чего-либо еще.

Одним из простых способов обеспечения выравнивания является использование std::max_align_t как это:

union Chunk
{
std::max_align_t max_align;
char mem[sizeof(Item)];
};

Что сделаю Chunk::mem выровнен для любого стандартного встроенного типа.


Другой способ — использовать точное выравнивание типа, который вы хотели бы поместить в char массив с использованием C ++ 11 alignas ключевое слово:

struct Chunk
{
alignas(Item) char mem[sizeof(Item)];
};

И это именно то, что std::aligned_storage делает для вас.

Однако это потребует раскрытия определения Item, что может быть неудобно или нежелательно в некоторых случаях.

Например, этот метод может быть использован в качестве оптимизации для Идиома однако, чтобы избежать выделения памяти, это потребовало бы раскрытия определения реализации в заголовочном файле, чтобы получить его размер и выравнивание, что противоречит цели Pimpl. Увидеть Быстрая идиома Pimpl Больше подробностей.


Еще одна деталь, если вы хотите скопировать / переместить Chunkи ожидать, что сохраненные объекты останутся действительными, эти сохраненные объекты должны быть тривиально копируемый, например

static_assert(std::is_trivially_copyable<Item>::value,
"Item cannot be copied byte-wise");
typedef std::aligned_storage<sizeof(Item), alignof(Item)> Chunk;

Для пула памяти std::vector<Chunk> это не лучший выбор, потому что при перераспределении, когда вектор растет, все указатели и ссылки на объекты, хранящиеся в пуле, становятся недействительными.

Объекты не должны перемещаться в общем пуле памяти.

4

Другие решения

Других решений пока нет …