web-dev-qa-db-ja.com

c ++ 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を効率的に実装できますか?

22
Łukasz Lew

ただしてください:

_#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()の呼び出しが引き続き必要です。

39
Andy Prowl

私はこれを受け入れられた答えよりも少し好むでしょう:

#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;
}
15
Daniel

@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 &&)。

2
mfnx