#c #c 11 #iterator #move #stdvector
#c #c 11 #итератор #переместить #stdvector
Вопрос:
Рассмотрим следующий фрагмент (видел что-то аналогичное этому в большом коде моделирования)
std::vector<int> v1{1,2,3,4,5,6,7};
std::vector<int> v2;
std::move(v1.begin() 2, v1.end(), back_inserter(v2));
Здесь я перемещаю диапазон элементов из v1
в v2
, но есть ли какое-либо особое преимущество в этом по сравнению с копированием? На самом деле я не вижу, в чем было бы преимущество move
здесь, поскольку оно работает с диапазоном int
s. На самом деле, я не верю, что происходит какое-либо перемещение, поскольку мы имеем дело с типами POD.
Если бы мы вместо этого хотели перенести все v1
в v2
, то мы могли бы сделать:
v2 = std::move(v1);
Приведение здесь позволило бы v2
теперь владеть указателем на непрерывный диапазон памяти, ранее принадлежавший v1
, таким образом избегая копирования.
Но в первом перемещении диапазона элементов я не вижу пользы.
Комментарии:
1. Подумайте о векторе векторов
2. @Mat Верно, в этом случае я вижу выгоду. Я просто не вижу пользы в приведенном выше случае. Код, который я видел, действительно был вектором целых чисел, перемещение которого применялось к непрерывному диапазону элементов в векторе.
3. Как вы правильно заметили, семантика перемещения для модулей совпадает с семантикой копирования. Для типа, который фактически более эффективно реализует конструктор перемещения и / или назначение перемещения, вы бы заметили разницу?
4. Иногда вам нужно копировать, иногда вам нужно перемещать. Для этого конкретного варианта использования ваш вызов
std::move
(в первом примере) семантически ничем не отличается отstd::copy
последующегоv1.erase(...)
, но зачем это делать, когда одного вызоваstd::move
было бы достаточно?
Ответ №1:
Здесь я перемещаю диапазон элементов из
v1
вv2
, но есть ли какое-либо особое преимущество в этом по сравнению с копированием?
Нет. Здесь все произошло просто из-за изменения диапазона, потому что ваше использование std::move
для примитивных типов просто справляется. Поэтому он делает то же самое, что и простой, если бы у вас было:
std::vector<int> v2{v1.begin() 2, v1.end()};
Следовательно, вы правы в выводах. Однако это называется фундаментальные типы / примитивные типы, а не модули.
Но в первом перемещении диапазона элементов я не вижу пользы.
Рассмотрим случай std::vector</*expensive copy type*/>
, в котором имеет смысл перемещать базовые элементы диапазона, когда это возможно.
Например, рассмотрим std::vector<std::string>
случай
std::vector<std::string> v1{ "1","2","3","4","5","6","7" };
std::vector<std::string> v2;
// reserve memory for unwanted reallocations
v2.reserve(std::distance(v1.begin() 2, v1.end()));
// moves the string element in the range
std::move(v1.begin() 2, v1.end(), back_inserter(v2));
// v1 now: 1 2
// v2 now: 3 4 5 6 7
В качестве дополнительного примечания, вместо std::move
диапазона в отдельной строке, для итераторов можно также использовать std::make_move_iterator
, для построения диапазона перемещения при объявлении (если сделать ошибку).
#include <iterator> // std::make_move_iterator
std::vector<std::string> v1{ "1","2","3","4","5","6","7" };
std::vector<std::string> v2{ std::make_move_iterator(v1.begin() 2),
std::make_move_iterator(v1.end()) };