Почему std::vector::emplace вызывает деструктор без вызова конструктора копирования?
Я храню предметы внутри std::vector
и я хочу, чтобы как можно больше не вызывать деструктор.
Я заменил конструктор копирования и назначения на перемещения:
class Object
{
Object(const Object&) = delete;
Object(Object&&);
Object& operator=(const Object&) = delete;
Object& operator=(Object&&);
[...]
};
Я инициализирую это так:
std::vector<Object> container;
container.reserve(42) // Reserve a lot in order to be sure it won't be a problem
Затем я добавляю два элемента с помощью emplace_back (конструктор занимает один int
параметр):
container.emplace_back(1);
container.emplace_back(3);
Пока там все нормально. Но затем я хочу вставить элемент перед последним с помощью emplace:
auto it = container.end();
it--; // Last position.
it--; // Before last position.
container.emplace(it, 2);
Но здесь деструктор называется.
Я пытался найти, почему с Valgrind, кажется, emplace
вызовы функций _M_insert_aux
это называется мой деструктор.
Как я мог избежать этого?
1 ответ
Вы не можете избежать этого. Это просто как vector
работает. Это непрерывный массив. Единственный способ вставить новый элемент в непрерывный массив - это переместить старые элементы вниз. Это означает, что вы используете перемещение, чтобы переместить их на новые позиции.
Так что если у вас есть следующий вектор и их содержимое:
[5][12][16]
Если вы вставите после второго элемента, то в какой-то момент у вас есть это:
[5][12][*][16]
Где "*" - это значение элемента, из которого был сделан переход.
Затем наступает emplace
, emplace
явно создаст значение на месте; вот для чего это. Тем не менее, в третьем элементе уже есть живой объект: значение "от".
Следовательно, этот объект должен быть уничтожен до того, как новый объект будет построен на его месте. Следовательно, деструктор должен быть вызван.
Если вы использовали insert
скорее, чем emplace
тогда у тебя все равно будет деструктор. Но это будет деструктор объекта, который вы передаете insert
функция.
Так что где-то будет "лишний" деструктор.
Но на самом деле, вы не должны беспокоиться о количестве вызовов деструкторов. Фокус на абсолютных затратах. Вообще говоря, если у вас есть тип "только для перемещения", деструктор для значения "перемещенный из" будет дешевым.