このスニペットを検討してください:
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
を効率的に実装できますか?
ただしてください:
_#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()
の呼び出しが引き続き必要です。
私はこれを受け入れられた答えよりも少し好むでしょう:
#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;
}
@Danielの答えを少し改善しようとしているだけです。関数を2回定義するのではなく、ソースを値で渡す必要があります。
// 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 &&)。