Самописный std::vector, когда нужно вызывать конструкторы/деструкторы элементов?
Я изобретаю велосипед (самописный аналог std::vector). В каких случаях необходимо вызывать конструкторы/деструкторы элементов, а в каких достаточно просто копировать память? С тривиальными типами всё понятно. С нетривиальными сложнее. Пример: оператор присваивания (operator=(const std::vector& other)) очевидно, что нужно уничтожить старые элементы и создать новые, так как я создаю их копию.
Другой пример: удаление элемента в середине массива и сдвиг оставшихся. В этом случае новые копии не создаются. Нужно ли вызывать деструкторы для старых объектов и конструировать новые, или достаточно просто скопировать память?
Еще пример: при расширении массива и переносе данных в новое место — достаточно ли использовать memcpy, или необходимо вручную конструировать каждый объект?
Кажется, что конструировать новые объекты нет смысла, если они просто перемещаются в памяти, не создавая копий. Но, возможно, есть подводные камни? Единственный случай, который приходит на ум — если объект сохраняет указатель на самого себя, но это, скорее, проблема архитектуры самого объекта, а не контейнера.
Ответы (1 шт):
Проще всего всегда звать правильные конструкторы и 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 хотят добавить, но пока не добавили. Пока ждем, единственный выход - вручную составлять список типов, с которыми можно так делать.