Самописный 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 хотят добавить, но пока не добавили. Пока ждем, единственный выход - вручную составлять список типов, с которыми можно так делать.