2つのstd :: vectorsを連結する


686

2つstd::vectorのを連結するにはどうすればよいですか?


5
与えられた答えは実際には連結されません。彼らはコピーを追加します。(効率の観点から)std :: vector連結メソッドを作成するための使用法があるかもしれませんが、ノードの管理の高度な共有が必要になるため、おそらくそれが行われていません。
FauChristian 2017

8
@FauChristian:いいえ、効率の観点からは使用できない可能性があります。ベクトルメモリは連続的でなければならないので、提案されていることは不可能です。「ノードの管理の高度な共有」が必要で、ベクトルクラスをこのように変更すると、両端キューが発生します。それでも、提案された方法でメモリを再利用することは非常に困難ですが、少しは実現可能になるでしょう。現在実装されているとは思いません。主なことは、このような管理ノード(両端キュー)の共有では、エンドノードが部分的に空になっている可能性があるということです。
Cookie

4
@lecaruyer 2年前に尋ねられた質問を重複としてマークしたことに気付きました
eshirima

9
これが標準ライブラリとして、a + bまたはa.concat(b)標準ライブラリに実装されていないのはなぜですか?おそらく、デフォルトの実装は最適ではないかもしれませんが、すべての配列の連結をマイクロ最適化する必要はありません
oseiskar

9
長年の進化、あらゆる主流言語の最も高度なオペレーターのオーバーロード、言語の複雑さを2倍にするテンプレートシステム、それでも答えはv = v1 + v2ではありません。
Spike0xff

回答:


726
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );

53
最初に各ベクトルが保持する要素の数を取得するコードを追加し、vector1を最大の要素に設定します。そうでなければ、多くの不必要なコピーをしていることになります。
Joe Pineda

34
質問があります。vector1とvector2が同じベクトルである場合、これは機能しますか?
Alexander Rafferty、2011

6
複数のベクトルを1つに連結している場合、reserve最初に宛先ベクトルを呼び出すと役に立ちますか?
Faheem Mitha

33
@AlexanderRafferty:の場合のみvector1.capacity() >= 2 * vector1.size()。あなたが呼ばない限り、これは非定型ですstd::vector::reserve()。そうでない場合はベクターがパラメータ2および3として渡されたイテレータを無効化、再割り当てされます
ドリューDormann氏

28
標準ライブラリにもっと簡潔な表現がないのは残念です。.concatまたは+=何か
nmr 2016年

193

C ++ 11を使用しstd::move_iteratorていて、単にコピーするのではなく要素を移動したい場合は、挿入(またはコピー)と一緒に使用できます。

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<int> dest{1,2,3,4,5};
  std::vector<int> src{6,7,8,9,10};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  // Print out concatenated vector.
  std::copy(
      dest.begin(),
      dest.end(),
      std::ostream_iterator<int>(std::cout, "\n")
    );

  return 0;
}

これは、intを使用した例では効率が悪くなります。それらを移動することは、コピーするよりも効率的ではないためです。ただし、最適化された移動を使用するデータ構造では、不要な状態のコピーを回避できます。

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
  std::vector<std::vector<int>> src{{6,7,8,9,10}};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  return 0;
}

移動後、srcの要素は未定義ですが破壊しても安全な状態にあり、以前の要素は最後に直接destの新しい要素に転送されました。


6
std :: make_move_iterator()メソッドは、std :: unique_ptrのstd :: vectorsを連結するときに役立ちました。
Knitschi、2014

これとはstd::move(src.begin(), src.end(), back_inserter(dest))どう違いますか?
kshenoy


77

または、次のように使用できます。

std::copy(source.begin(), source.end(), std::back_inserter(destination));

std :: back_inserterの代わりに何かを使用して1つのタイプから別のタイプに変換できるため、このパターンは2つのベクトルにまったく同じタイプのものが含まれていない場合に役立ちます。


7
コピー方法はあまり良い方法ではありません。これはpush_backを複数回呼び出します。つまり、多くの要素を挿入する必要がある場合、これは複数の再割り当てを意味する可能性があります。ベクトルの実装は再割り当てを回避するために最適化を行うことができるため、挿入を使用することをお勧めします。コピーを開始する前にメモリを予約できます
Yogesh Arora

7
@ヨゲシュ:承知しましたが、reserve最初に電話をかけるのを止めるものはありません。std::copyが便利な理由は、以外のものを使用したい場合ですback_inserter
Roger Lipscombe 2010年

「複数の割り当て」と言うと、それは本当です-割り当ての数は最悪のログ(追加されたエントリの数)です-つまり、エントリを追加するコストは、追加されたエントリの数で一定です。(基本的に、プロファイリングで予約が必要であることが示されない限り、心配する必要はありません)。
Martin Bonnerがモニカをサポートする

代わりにstd :: transformを使用してこれを行うことができます。
マーティンブロードハースト2016

1
コピーは、リザーブがあっても、かなり吸います。vector :: insertはすべてのチェックを回避します:quick-bench.com/bLJO4OfkAzMcWia7Pa80ynwmAIA
Denis Yaroshevskiy

63

C ++ 11では、ベクトルbをaに追加することをお勧めします。

std::move(b.begin(), b.end(), std::back_inserter(a));

ときab重なっていない、そしてbもう使用する予定がありません。


これがあるstd::moveから<algorithm>、ではない普通の std::moveから<utility>


10
aがbである場合の未定義の動作(これが起こらないことがわかっている場合は問題ありませんが、汎用コードで注意する価値があります)。
Martin Bonnerがモニカをサポートする

1
@MartinBonner言及していただきありがとうございます。おそらく、私insertはより安全な古い方法に戻るべきです。
2016

15
ああ、その他のstd :: move。初めて見たときはかなり混乱します。
xaxxon

1
これinsert()move_iterators とは異なりますか?もしそうなら、どうですか?
GPhilo 2018年

1
std::moveほとんどの人はこの過負荷を知らないので、ここで話していることについてのメモを追加しました。それが改善であることを願っています。
YSC 2018年


24

私はすでに言及されているものを好む:

a.insert(a.end(), b.begin(), b.end());

ただし、C ++ 11を使用する場合、もう1つの一般的な方法があります。

a.insert(std::end(a), std::begin(b), std::end(b));

また、質問の一部ではありませんが、reserveパフォーマンスを向上させるために追加する前に使用することをお勧めします。また、ベクトルをそれ自体と連結している場合、予約せずに失敗するため、常にを使用する必要がありreserveます。


だから基本的にあなたが必要とするもの:

template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
    a.reserve(a.size() + b.size());
    a.insert(a.end(), b.begin(), b.end());
}

2
std::引数に依存するルックアップを通じて推定されますend(a)十分でしょう
Asu

4
@Asu ADLはstd::、のタイプaがからのものstdである場合にのみ追加されます。これは一般的な側面を無効にします。
Potatoswatter 2016

いい視点ね。この場合はベクターなので、とにかく機能しますが、はい、それがより良い解決策です。
Asu

std :: begin()/ end()は、それらをメンバー関数として持たないコレクション(配列など)に追加されました。しかし、配列にはinsert()メンバー関数もありません。また、「insert()があるがbegin()がないコレクションがあります(これはstd :: begin()で動作します)」という質問を呼び出します。
James Curran 2018年


12

あなたはvector :: insertを使うべきです

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

2
これは、2008年にトムリッターとロバートギャンブルによって与えられた答えと同じではありませんか?
IgNite

9

一般的なパフォーマンスの向上 CONCATENATEのためには、ベクトルの大きさをチェックすることです。そして、小さい方を大きい方とマージ/挿入します。

//vector<int> v1,v2;
if(v1.size()>v2.size()) {
    v1.insert(v1.end(),v2.begin(),v2.end());
} else {
    v2.insert(v2.end(),v1.begin(),v1.end());
}

とてもシンプルですが、そのように考えたことはありません!
Zimano

2
サンプルコードが正しくありません。の位置を指定するためにv1.insert(v2.end()...イテレータinto v2を使用していますv1
David Stone、

クイックスワップを使用することもできます。@DavidStone連結の順序が変更されるように編集しました。ベクトルの先頭に追加することは可能ですか?
qwr

最初に挿入できますが、遅くなります。ただし、実際に「連結」するには、通常、順序が重要であるため、それを行う必要があります。
David Stone、

7

ベクトルを簡潔に連結できるようにしたい場合は、+=演算子をオーバーロードすることができます。

template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
    vector1.insert(vector1.end(), vector2.begin(), vector2.end());
    return vector1;
}

その後、次のように呼び出すことができます。

vector1 += vector2;

6

強力な例外保証に関心がある場合(コピーコンストラクターが例外をスローできる場合):

template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
    const auto orig_v1_size = v1.size();
    v1.reserve(orig_v1_size + v2.size());
    try
    {
        v1.insert(v1.end(), v2.begin(), v2.end());
    }
    catch(...)
    {
        v1.erase(v1.begin() + orig_v1_size, v1.end());
        throw;
    }
}

append_moveベクトル要素のmoveコンストラクターがスローできる場合、強力な保証付きの同様のものを一般に実装することはできません(可能性は低いですが、それでも)。


それは可能ではありませんv1.erase(...あまりにもスローしますか?
クラススケルトン

insertすでにこれを処理します。また、このへの呼び出しeraseはと同等resizeです。
Potatoswatter 2016

私はこれが好きです-ありがとう!
natersoz

5

これをヘッダーファイルに追加します。

template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
    vector<T> ret = vector<T>();
    copy(a.begin(), a.end(), back_inserter(ret));
    copy(b.begin(), b.end(), back_inserter(ret));
    return ret;
}

次のように使用します。

vector<int> a = vector<int>();
vector<int> b = vector<int>();

a.push_back(1);
a.push_back(2);
b.push_back(62);

vector<int> r = concat(a, b);

rには[1,2,62]が含まれます


これが反対投票された理由がわかりません。これは、これを行う最も効率的な方法ではないかもしれませんが、間違いではなく、効果的です。
leeor_net 2016年

4

次に、C ++ 11移動セマンティクスを使用した汎用ソリューションを示します。

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    if (lhs.empty()) return rhs;
    if (rhs.empty()) return lhs;
    std::vector<T> result {};
    result.reserve(lhs.size() + rhs.size());
    result.insert(result.cend(), lhs.cbegin(), lhs.cend());
    result.insert(result.cend(), rhs.cbegin(), rhs.cend());
    return result;
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
    lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
    return std::move(lhs);
}

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
    rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
    return std::move(rhs);
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
    if (lhs.empty()) return std::move(rhs);
    lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
    return std::move(lhs);
}

注どのから、この異なりappendにするvector


4

+演算子用に独自のテンプレートを準備できます。

template <typename T> 
inline T operator+(const T & a, const T & b)
{
    T res = a;
    res.insert(res.end(), b.begin(), b.end());
    return res;
}

次のこと-+を使用するだけ:

vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
    cout << x << " ";
cout << endl;

この例は出力を示します:

1 2 3 4 5 6 7 8

2
使用するのT operator+(const T & a, const T & b)は危険vector<T> operator+(const vector<T> & a, const vector<T> & b)です。使用することをお勧めします。
Matthieu H

4

アルゴリズムがあるstd::mergeから、C ++ 17の非常に使いやすいです、

以下に例を示します。

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    //DATA
    std::vector<int> v1{2,4,6,8};
    std::vector<int> v2{12,14,16,18};

    //MERGE
    std::vector<int> dst;
    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));

    //PRINT
    for(auto item:dst)
        std::cout<<item<<" ";

    return 0;
}

2
私はそれがより使用に何らかの簡単だとは思わないstd::vector::insertが、それは別の何かを行います。他の終わりに1つのベクトルを挿入する対新しい範囲に二つの範囲をマージ。答えで言及する価値がありますか?
JB

4

読み取り専用の目的で値の範囲を反復するだけの場合は、両方のベクトルをコピー(O(n))する代わりにプロキシ(O(1))にラップするので、すぐに確認できます。単一の連続したものとして。

std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };

VecProxy<int> AB(A, B);  // ----> O(1)!

for (size_t i = 0; i < AB.size(); i++)
    std::cout << AB[i] << " ";  // ----> 1 2 3 4 5 10 20 30

「VecProxy」の実装や長所と短所など、詳細については、https: //stackoverflow.com/a/55838758/2379625を参照してください


3
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));

6
このコードスニペットは問題を解決する可能性がありますが、なぜまたはどのように質問に答えるかは説明していません。コードの説明を含めてください。これは、投稿の品質を向上させるのに役立ちます。報告者/レビュアー: このようなコードのみの回答の場合は、投票しないでください。削除しないでください。(注:この回答は、実際には説明を作成するのに十分単純なので、反対投票は不要です。NAA/ VLQフラグを回避するために説明を追加することもできます。)
Scott Weldon

2

任意の数のコンテナーを連結するこの関数を実装しました。右辺値参照から移動し、それ以外の場合はコピーします

namespace internal {

// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
    // Currently, require each homogenous inputs. If there is demand, we could probably implement a
    // version that outputs a vector whose value_type is the common_type of all the containers
    // passed to it, and call it ConvertingConcatenate.
    static_assert(
            std::is_same_v<
                    typename std::decay_t<Target>::value_type,
                    typename std::decay_t<Head>::value_type>,
            "Concatenate requires each container passed to it to have the same value_type");
    if constexpr (std::is_lvalue_reference_v<Head>) {
        std::copy(head.begin(), head.end(), std::back_inserter(*target));
    } else {
        std::move(head.begin(), head.end(), std::back_inserter(*target));
    }
    if constexpr (sizeof...(Tail) > 0) {
        AppendNoReserve(target, std::forward<Tail>(tail)...);
    }
}

template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
    if constexpr (sizeof...(Tail) > 0) {
        return head.size() + TotalSize(tail...);
    } else {
        return head.size();
    }
}

}  // namespace internal

/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
    size_t totalSize = internal::TotalSize(head, tail...);
    std::vector<typename std::decay_t<Head>::value_type> result;
    result.reserve(totalSize);
    internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
    return result;
}

1

あなたが探しているものがベクトルを作成後に別のものに追加する方法であるならば、あなたvector::insertはあなたの最善の策です、例えば、何度も答えられました:

vector<int> first = {13};
const vector<int> second = {42};

first.insert(first.end(), second.cbegin(), second.cend());

残念ながらconst vector<int>、上記のようにを作成する方法はありませんinsert


実際に探しているものがこれら2つvector<int>のの連結を保持するためのコンテナである場合、次の条件に当てはまる場合は、より良いものがあるかもしれません。

  1. あなたのvectorプリミティブが含まれています
  2. 含まれているプリミティブのサイズが32ビット以下である
  3. constコンテナが欲しい

上記にすべて該当する場合は、私が使用することをお勧めしたいbasic_stringです誰がchar_typeあなたの中に含まれるプリミティブのサイズと一致しますvectorstatic_assertこれらのサイズが一貫していることを検証するには、コードにを含める必要があります。

static_assert(sizeof(char32_t) == sizeof(int));

この保持が真の場合、次のことができます。

const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

間の違いの詳細については、stringvectorあなたがここに見ることができます:https://stackoverflow.com/a/35558008/2642059

このコードの実際の例については、こちらをご覧ください。http//ideone.com/7Iww3I


0

この解決策は少し複雑かもしれませんが、boost-range他にも提供できる優れた点がいくつかあります。

#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    boost::copy(b, std::back_inserter(a));
    for (auto& iter : a) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

多くの場合、もの意図は、ベクトルを組み合わせることであるabだけ反復それ以上のいくつかの操作を行っています。この場合、とんでもない単純なjoin関数があります。

#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    std::vector<int> c = { 7,8,9 };
    // Just creates an iterator
    for (auto& iter : boost::join(a, boost::join(b, c))) {
        std::cout << iter << " ";
    }
    std::cout << "\n";
    // Can also be used to create a copy
    std::vector<int> d;
    boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
    for (auto& iter : d) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

大きなベクターの場合、コピーがないため、これは利点になる可能性があります。Generalizeを複数のコンテナーに簡単にコピーするためにも使用できます。

何らかの理由でboost::join(a,b,c)、のようなものはありません。


0

ポリモーフィック型の使用のためのテンプレートを使用して、事前に実装されたSTLアルゴリズムでそれを行うことができます。

#include <iostream>
#include <vector>
#include <algorithm>

template<typename T>

void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){

     for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}

int main()
{
    std::vector<int> values_p={1,2,3,4,5};
    std::vector<int> values_s={6,7};

   concat(values_p, values_s);

    for(auto& it : values_p){

        std::cout<<it<<std::endl;
    }

    return 0;
}

さらに使用したくない場合は、2番目のベクトルをクリアできます(clear()メソッド)。


-3

正直に言うと、2つのベクターから別のベクターに要素をコピーするか、2つのベクターの1つだけを追加することで、2つのベクターをすばやく連結できます。それはあなたの目的に依存します。

方法1:サイズが新しいベクターを割り当てるには、2つの元のベクターのサイズの合計を使用します。

vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector

方法2:ベクトルBの要素を追加/挿入して、ベクトルAを追加します。

// Loop for insert elements of vector_B into vector_A with insert() 
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());

4
他の回答でまだ提供されていないあなたの回答は何を追加しますか?
2016

13
@マット:太字。
marcv81 2017年

後で元のベクトルが不要になった場合は、std::move_iterator要素をコピーするのではなく移動するように使用することをお勧めします。(en.cppreference.com/w/cpp/iterator/move_iteratorを参照してください)。
tmlen 2018

なにsetcapacity?なにfunction:
LF

@LF私は彼がそのresize方法について話していると思います。
Matthieu H
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.