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

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

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));
});

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

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

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

  1. 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));
    
  2. 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.

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

Вы используете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.

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