Есть ли стандартный способ перемещения диапазона в вектор?

Рассмотрим следующую программу, которая вставляет диапазон элементов в вектор:

vector<string> v1;
vector<string> v2;

v1.push_back("one");
v1.push_back("two");
v1.push_back("three");

v2.push_back("four");
v2.push_back("five");
v2.push_back("six");

v1.insert(v1.end(), v2.begin(), v2.end());

Это эффективно копирует диапазон, выделяя достаточно места в целевом векторе для всего диапазона, так что потребуется максимум одно изменение размера. Теперь рассмотрим следующую программу, которая пытается переместить диапазон в вектор:

vector<string> v1;
vector<string> v2;

v1.push_back("one");
v1.push_back("two");
v1.push_back("three");

v2.push_back("four");
v2.push_back("five");
v2.push_back("six");

for_each ( v2.begin(), v2.end(), [&v1]( string & s )
{
    v1.emplace_back(std::move(s));
});

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

Итак, мой вопрос, есть ли эквивалент вставки, который может переместить диапазон в вектор?

 Ben Voigt23 мая 2012 г., 14:49
@Benj:std::vector Итераторы имеют произвольный доступ, но библиотека может не включать оптимизацию. И возникает вопрос о вставке в вектор из произвольного неопределенного диапазона, который может не иметь итераторов с произвольным доступом.
 rubenvb23 мая 2012 г., 14:43
Если вам нужно предварительно выделить место в векторе, используйтеstd::vector::reserve, и хранитьpush_back/emplace_back.
 Ben Voigt23 мая 2012 г., 14:46
Это будет необязательной оптимизацией, возможной только тогда, когда диапазон определяется итераторами с произвольным доступом. Не рассчитывай на это.
 Benj23 мая 2012 г., 14:46
@rubenvb Да, я подумал, что это, вероятно, ответ, просто позор, что нет такого чистого метода, какinsert() является.
 Benj23 мая 2012 г., 14:47
Это всегда так при использованииstd::vector нет?

Ответы на вопрос(2)

Решение Вопроса

move_iterator сinsert:

v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end()));

Пример в 24.5.3 почти такой.

Вы получите нужную оптимизацию, если (а)vector::insert использует диспетчер тега итератора для обнаружения итератора с произвольным доступом и предварительного расчета размера (который, как вы предполагали, он делает в своем примере, который копирует), и (b)move_iterator сохраняет категорию итератора для итератора, который он переносит (что требуется стандартом).

Неясный момент: я уверен, чтоvector::insert Можно использовать исключение из источника (что здесь не имеет значения, поскольку источник того же типа, что и пункт назначения, поэтому emplace аналогичен копированию / перемещению, но будет иметь отношение к другим идентичным примерам). Я еще не нашел утверждения, что для этого требуется, я просто сделал вывод из того факта, что требование к паре итераторовi,j перешел кinsert в том, чтоT бытьEmplaceConstructible от*i.

 23 мая 2012 г., 15:07
+1 для фона
 29 сент. 2017 г., 13:58
@ Евгений: работает для меня, поэтому я, вероятно, неправильно понял, что вы имеете в виду. Я предлагаю вам задать новый вопрос, включая код, который вам не подходит.
 Benj23 мая 2012 г., 15:11
Хм, я обнаружил, что также можно использоватьmake_move_iterator превратитьstd::copy_if в эквивалентеstd::move_if, Это очень удобно.
 28 сент. 2017 г., 01:37
Извините, что воскресил это и задал вопрос в комментариях, но что, если исходным и целевым векторами будут вектор & lt; unique_ptr & lt; T & gt; & gt; ? применение make_move_iterator к 1-му аргументу не будет работать, и без него возникнет ошибка при копировании. как я должен идти об этом?
 Benj23 мая 2012 г., 14:50
Отлично, не знал оmake_move_iterator

std::move algorithm with preallocation:

#include <iterator>
#include <algorithm>

v1.reserve(v1.size() + v2.size()); // optional
std::move(v2.begin(), v2.end(), std::back_inserter(v1));

The following would be more flexible yet:

v1.insert(v1.end(), 
     std::make_move_iterator(v2.begin()), 
     std::make_move_iterator(v2.end()));

Steve Jessop provided background information on precisely what it does and probably how it does so.

 23 мая 2012 г., 14:48
@BenVoigt Почему? Я полагаю, что это до реализации. вектор :: резерв твой друг. Позвольте мне проверить стандарт.
 Benj23 мая 2012 г., 14:48
О, аккуратно, не знал об этой формеstd::move, Хотя я думаю,back_inserter все еще может вызвать несколько размеров, хотя.
 23 мая 2012 г., 14:51
Я не думаю, что первый реально может перераспределить только один раз:move может видеть, что у вас есть итератор произвольного доступа, так что он может определить необходимый размер, но он видит толькоback_insert_iterator, а не основной вектор, поэтому он не имеет возможности зарезервировать пространство. Это потребовало бы невероятно извилистой перегрузкиstd::move чтобы поймать это дело.
 23 мая 2012 г., 14:50
@sehe:back_inserter не знает a priori, сколько раз он будет вызван.std::move может узнать, но нет никакого способа узнать, что адресат вставляется в вектор.
 23 мая 2012 г., 14:46
Шансы на изменение размера места назначенияvector только один раз очень, очень худой.

Ваш ответ на вопрос