Эффективно перезаписать std::vector<T> другим std::vector<T>

#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 не работает, поскольку меняет размер вектора.

Если old и new всегда одинакового размера, зачем new = std::move(old) менять размер new? Это самая эффективная перезапись, которую я могу придумать. Есть ли у вас доказательства (например, профилирование), которые говорят об обратном в вашем коде?

Cory Kramer 30.08.2024 15:57

Звучит как какая-то устаревшая микрооптимизация. Есть ли у вас что-нибудь, что измеряет производительность? Вы уверены, что правильно определили узкое место?

Marek R 30.08.2024 15:58

@CoryKramer В приведенном выше примере после new = std::move(old) у нас есть old.size() = 0 Оба вектора должны сохранять свой размер, поскольку от этого зависит интерфейс «перевычисления».

Mathieu 30.08.2024 16:00

Если вы просто хотите избежать перемещения данных при перемещении достаточно большого количества элементов, вы можете зарезервировать() некоторое пространство перед алгоритмом.

zerocukor287 30.08.2024 16:07

Кстати, size() и capacity() не одно и то же. size() сообщит вам, сколько элементов содержит вектор — если вы переместите все из вектора old, он не будет содержать никаких элементов.

zerocukor287 30.08.2024 16:10

Не беспокойтесь о своих размерах. Move один вектор в другой; нет ничего более эффективного, кроме использования указателей и их замены. Если вы уже знаете, сколько элементов вам нужно, или можете дать оценку, не тратя слишком много памяти, reserve() нужное вам пространство. Это ничего не дает, если в векторе уже достаточно места.

Christian Stieber 30.08.2024 16:17

@ChristianStieber Как бы мне это сделать? Например, при «перевычислении», которое я делаю, sourceVector[0] = ...результат сбоя после destVector = std::move(sourveVector) ?

Mathieu 30.08.2024 16:24

Как было предложено в нескольких комментариях, разве вам не нужно сохранять емкость, а не размер (в этом случае ответ HolyBlackCat идеален)? Если да, пожалуйста, отредактируйте свой вопрос.

Oersted 30.08.2024 16:31

@Эрстед, я на самом деле не знаю. Как я уже сказал, я хотел бы переместить source в dest. После этой операции я хочу смело писать source[2] = ..., учитывая, что source.size() = 3 перед переездом.

Mathieu 30.08.2024 16:35

@Oersted Они хотят сохранить размер. И ответ HolyBlackCat делает это, учитывая первоначальное ограничение sourceVector.size() == destinationVector.size().

Yksisarvinen 30.08.2024 16:40

@Yksisarvinen Действительно, я упустил из виду предварительное условие относительно размеров.

Oersted 30.08.2024 16:42

Move сохранит capacity выше начального size. Но если вам нужен not source.empty(), то вам подойдет старый dest.swap(source);.

Red.Wave 30.08.2024 18:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
12
125
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
std::swap(sourceVector, destinationVector);

или std::ranges::swap(sourceVector, destinationVector); если вам хочется (что делает то же самое).

Есть ли какая-то конкретная причина и в чем проблема с подходом ОП? (Не специально для меня, а для других, читающих этот ответ в будущем)

Dan Mašek 30.08.2024 16:05

Работает, но не делается ли std::swapвнутренне копия исходного или конечного источника перед заменой элементов? В моем случае эта копия была бы ненужной. Как отмечали другие, я не разработал надлежащую меру производительности, но хочу использовать тот факт, что меня не волнуют значения источника после присвоения.

Mathieu 30.08.2024 16:08

Сами @DanMašek OP сказали, что «должно быть гарантировано... что размер new не изменится. ... Очевидно, std::move не работает, потому что он меняет размер вектора».

HolyBlackCat 30.08.2024 16:08

@DanMašek Подход OP, по сути, destinationVector = std::move(sourceVector), который не гарантирует, что sourceVector.size() не изменится - даже если заранее sourceVector и destinationVector имеют одинаковый размер. Помимо того, что выход sourceVector — это состояние, в котором его можно безопасно уничтожить, такой подход почти не дает никаких гарантий относительно состояния sourceVector.

Peter 30.08.2024 16:10

@Mathieu swap будет перемещаться, когда это возможно (IIRC, когда доступно назначение перемещения). То есть swap(x, y) становится T a = std::move(x); x = std::move(y); y = std::move(a);, что по сути просто переставляет некоторые указатели.

molbdnilo 30.08.2024 16:13

@Mathieu В случае сомнений: en.cppreference.com/w/cpp/container/vector/swap Обменяет содержимое и емкость контейнера другими. Не вызывает никаких операций перемещения, копирования или замены отдельных элементов. Так что я согласен с молдбнило... эффективнее этого быть не может.

Pepijn Kramer 30.08.2024 16:18

@Питер Да, я знаю, я надеялся увидеть это в ответе. И имеет смысл, чтобы вектор перемещения из был пустым, поскольку в противном случае вам пришлось бы платить за (потенциально огромное) выделение чего-то, что вы не указали, что вам нужно (и в большинстве случаев не будет).

Dan Mašek 30.08.2024 16:19

Другие вопросы по теме