Как использовать семантику перемещения С++ 11 для добавления векторного содержимого в другой вектор?
Рассмотрим этот фрагмент:
class X;
void MoveAppend(vector<X>& src, vector<X>& dst) {
dst.reserve(dst.size() + src.size());
for (const X& x : src) dst.push_back(x);
src.clear();
}
Если мы предположим, что class X
реализует семантику перемещения, как я могу эффективно реализовать MoveAppend
?
Ответы
Ответ 1
Просто выполните:
#include <iterator>
#include <algorithm>
// ...
void MoveAppend(std::vector<X>& src, std::vector<X>& dst)
{
if (dst.empty())
{
dst = std::move(src);
}
else
{
dst.reserve(dst.size() + src.size());
std::move(std::begin(src), std::end(src), std::back_inserter(dst));
src.clear();
}
}
Если dst
пуст, присваивание переходов от src
до dst
выполнит задание - это будет так же дешево, как и может быть, просто "украсть" массив, инкапсулированный src
, чтобы dst
укажет на это позже.
Если dst
не пуст, элементы, добавленные к dst
, будут перемещены по контуру из элементов в src
. После вызова std::move()
, src
не будет пустым - он будет содержать "зомби", перемещенный из элементов. Поэтому вызов clear()
по-прежнему необходим.
Ответ 2
Я бы немного предпочел это на принятый ответ:
#include <vector>
#include <iterator>
#include <utility>
template <typename T>
typename std::vector<T>::iterator append(const std::vector<T>& src, std::vector<T>& dest)
{
typename std::vector<T>::iterator result;
if (dest.empty()) {
dest = src;
result = std::begin(dest);
} else {
result = dest.insert(std::end(dest), std::cbegin(src), std::cend(src));
}
return result;
}
template <typename T>
typename std::vector<T>::iterator append(std::vector<T>&& src, std::vector<T>& dest)
{
typename std::vector<T>::iterator result;
if (dest.empty()) {
dest = std::move(src);
result = std::begin(dest);
} else {
result = dest.insert(std::end(dest),
std::make_move_iterator(std::begin(src)),
std::make_move_iterator(std::end(src)));
}
src.clear();
src.shrink_to_fit();
return result;
}
Пример:
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
const std::vector<std::string> v1 {"world", "!"};
std::vector<std::string> v2 {" "}, v3 {"hello"}, v4 {};
append(v1, v2); // copies
append(std::move(v2), v3); // moves
append(std::move(v3), v4); // moves
std::copy(std::cbegin(v4), std::cend(v4), std::ostream_iterator<std::string> {std::cout});
std::cout << std::endl;
}
Ответ 3
Просто пытаюсь немного улучшить ответ @Daniel: функция не должна быть определена дважды, источник должен быть передан по значению.
// std::vector<T>&& src - src MUST be an rvalue reference
// std::vector<T> src - src MUST NOT, but MAY be an rvalue reference
template <typename T>
inline void append(std::vector<T> source, std::vector<T>& destination)
{
if (destination.empty())
destination = std::move(source);
else
destination.insert(std::end(destination),
std::make_move_iterator(std::begin(source)),
std::make_move_iterator(std::end(source)));
}
Теперь вызывающая сторона может решить, копировать или переместить.
std::vector<int> source {1,2,3,4,5};
std::vector<int> destination {0};
auto v1 = append<int>(source,destination); // copied once
auto v2 = append<int>(std::move(source),destination); // copied 0 times!!
Никогда не используйте &&
для аргументов, кроме случаев, когда это необходимо (например: std :: ifstream &&).