Самописный std::vector, когда нужно вызывать конструкторы/деструкторы элементов?

Я изобретаю велосипед (самописный аналог std::vector). В каких случаях необходимо вызывать конструкторы/деструкторы элементов, а в каких достаточно просто копировать память? С тривиальными типами всё понятно. С нетривиальными сложнее. Пример: оператор присваивания (operator=(const std::vector& other)) очевидно, что нужно уничтожить старые элементы и создать новые, так как я создаю их копию.

Другой пример: удаление элемента в середине массива и сдвиг оставшихся. В этом случае новые копии не создаются. Нужно ли вызывать деструкторы для старых объектов и конструировать новые, или достаточно просто скопировать память?

Еще пример: при расширении массива и переносе данных в новое место — достаточно ли использовать memcpy, или необходимо вручную конструировать каждый объект?

Кажется, что конструировать новые объекты нет смысла, если они просто перемещаются в памяти, не создавая копий. Но, возможно, есть подводные камни? Единственный случай, который приходит на ум — если объект сохраняет указатель на самого себя, но это, скорее, проблема архитектуры самого объекта, а не контейнера.


Ответы (1 шт):

Автор решения: HolyBlackCat

Проще всего всегда звать правильные конструкторы и operator=. А если можно заменить их на memcpy, компилятор должен сам это сделать.

Если все-таки хотите сами звать memcpy, то это можно делать если std::is_trivially_...<T> == true выполняется для соответствующего конструктора (... = {copy,move}_constructible) или оператора присваивания ({copy,move}_assignable). true означает, что они копируют побайтово, поэтому можно самому копировать через memcpy.

Ну и для полноты, std::is_trivially_default_constructible_v<T> и std::is_trivially_destructible_v<T> означают, что дефолтный конструктор и деструктор соответственно можно не звать.

Кажется, что конструировать новые объекты нет смысла, если они просто перемещаются в памяти, не создавая копий. Но, возможно, есть подводные камни? Единственный случай, который приходит на ум — если объект сохраняет указатель на самого себя, но это, скорее, проблема архитектуры самого объекта, а не контейнера.

Нет, у объекта есть полное право так делать.

Есть еще термин "trivially relocatable" - т.е. такие типы, у которых мув-конструктор и деструктор нетривиальные, но комбинация "переместить у удалить оригинал" в итоге эквивалентна побайтовому копированию.

Стандартного способа проверить на trivially relocatable нет. В C++26 хотят добавить, но пока не добавили. Пока ждем, единственный выход - вручную составлять список типов, с которыми можно так делать.

→ Ссылка