#include <iostream>
#include <vector>
#include <utility>
// simple user-defined class
class MyClass {
public:
MyClass(int val) : value(val) {}
private:
int value;
};
int main() {
// Create and initialize sourceVector and destinationVector with MyClass instances
std::vector<MyClass> sourceVector = {MyClass(1), MyClass(2), MyClass(3)};
std::vector<MyClass> destinationVector = {MyClass(10), MyClass(20), MyClass(30)};
// Move elements from sourceVector to destinationVector
destinationVector = std::move(sourceVector);
// sourceVector.size() = 0 <-- not good
return 0;
}
Предположим, у нас есть std::vector<T> old
и std::vector<T> new
, которые гарантированно имеют одинаковый размер или оба не инициализированы. Цель — выполнить задание вида old = new
. После этого меня не волнует, какие значения имеет new
, поскольку я буду пересчитывать их. Однако необходимо гарантировать, что размер new
не изменится. Операция должна быть очень эффективной, поэтому я бы хотел избежать копирования (T
само по себе может быть большим объектом).
Очевидно, что std::move
не работает, поскольку меняет размер вектора.
Звучит как какая-то устаревшая микрооптимизация. Есть ли у вас что-нибудь, что измеряет производительность? Вы уверены, что правильно определили узкое место?
@CoryKramer В приведенном выше примере после new = std::move(old)
у нас есть old.size() = 0
Оба вектора должны сохранять свой размер, поскольку от этого зависит интерфейс «перевычисления».
Если вы просто хотите избежать перемещения данных при перемещении достаточно большого количества элементов, вы можете зарезервировать() некоторое пространство перед алгоритмом.
Кстати, size()
и capacity()
не одно и то же. size()
сообщит вам, сколько элементов содержит вектор — если вы переместите все из вектора old
, он не будет содержать никаких элементов.
Не беспокойтесь о своих размерах. Move
один вектор в другой; нет ничего более эффективного, кроме использования указателей и их замены. Если вы уже знаете, сколько элементов вам нужно, или можете дать оценку, не тратя слишком много памяти, reserve()
нужное вам пространство. Это ничего не дает, если в векторе уже достаточно места.
@ChristianStieber Как бы мне это сделать? Например, при «перевычислении», которое я делаю, sourceVector[0] = ...
результат сбоя после destVector = std::move(sourveVector)
?
Как было предложено в нескольких комментариях, разве вам не нужно сохранять емкость, а не размер (в этом случае ответ HolyBlackCat идеален)? Если да, пожалуйста, отредактируйте свой вопрос.
@Эрстед, я на самом деле не знаю. Как я уже сказал, я хотел бы переместить source
в dest
. После этой операции я хочу смело писать source[2] = ...
, учитывая, что source.size() = 3
перед переездом.
@Oersted Они хотят сохранить размер. И ответ HolyBlackCat делает это, учитывая первоначальное ограничение sourceVector.size() == destinationVector.size()
.
@Yksisarvinen Действительно, я упустил из виду предварительное условие относительно размеров.
Move сохранит capacity
выше начального size
. Но если вам нужен not source.empty()
, то вам подойдет старый dest.swap(source);
.
std::swap(sourceVector, destinationVector);
или std::ranges::swap(sourceVector, destinationVector);
если вам хочется (что делает то же самое).
Есть ли какая-то конкретная причина и в чем проблема с подходом ОП? (Не специально для меня, а для других, читающих этот ответ в будущем)
Работает, но не делается ли std::swap
внутренне копия исходного или конечного источника перед заменой элементов? В моем случае эта копия была бы ненужной. Как отмечали другие, я не разработал надлежащую меру производительности, но хочу использовать тот факт, что меня не волнуют значения источника после присвоения.
Сами @DanMašek OP сказали, что «должно быть гарантировано... что размер new не изменится. ... Очевидно, std::move
не работает, потому что он меняет размер вектора».
@DanMašek Подход OP, по сути, destinationVector = std::move(sourceVector)
, который не гарантирует, что sourceVector.size()
не изменится - даже если заранее sourceVector
и destinationVector
имеют одинаковый размер. Помимо того, что выход sourceVector
— это состояние, в котором его можно безопасно уничтожить, такой подход почти не дает никаких гарантий относительно состояния sourceVector
.
@Mathieu swap
будет перемещаться, когда это возможно (IIRC, когда доступно назначение перемещения). То есть swap(x, y)
становится T a = std::move(x); x = std::move(y); y = std::move(a);
, что по сути просто переставляет некоторые указатели.
@Mathieu В случае сомнений: en.cppreference.com/w/cpp/container/vector/swap Обменяет содержимое и емкость контейнера другими. Не вызывает никаких операций перемещения, копирования или замены отдельных элементов. Так что я согласен с молдбнило... эффективнее этого быть не может.
@Питер Да, я знаю, я надеялся увидеть это в ответе. И имеет смысл, чтобы вектор перемещения из был пустым, поскольку в противном случае вам пришлось бы платить за (потенциально огромное) выделение чего-то, что вы не указали, что вам нужно (и в большинстве случаев не будет).
Если
old
иnew
всегда одинакового размера, зачемnew = std::move(old)
менять размерnew
? Это самая эффективная перезапись, которую я могу придумать. Есть ли у вас доказательства (например, профилирование), которые говорят об обратном в вашем коде?