ループのないC ++で配列の内容をstd :: vectorにコピーするにはどうすればよいですか?


122

後で処理するために保存する必要があるプログラムの別の部分から関数に渡される値の配列があります。データを処理する前に関数が呼び出される回数がわからないため、動的ストレージ構造が必要なので、を選択しましたstd::vectorpush_backすべての値に対して個別に標準ループを実行する必要はありませんmemcpy。に似たものを使用してすべてをコピーできると便利です。

回答:


117

配列と配列サイズを取得した後でベクトルを構築できる場合は、次のように言うことができます。

std::vector<ValueType> vec(a, a + n);

...仮定aはあなたの配列でnあり、それに含まれる要素の数です。それ以外の場合は、std::copy()w / resize()がうまくいきます。

memcpy()値がプレーン・オールド・データ(POD)タイプであることが確実でない限り、私は離れたままにします。

また、これらのどれも実際にはforループを回避しないことにも注意してください。これは、コードでそれを表示する必要があるかどうかの問題です。値をコピーする場合、O(n)ランタイムのパフォーマンスは避けられません。

最後に、Cスタイルの配列はほとんどのSTLアルゴリズムにとって完全に有効なコンテナーです。生のポインターはと同等begin()であり、(ptr + n)はと同等end()です。


4
ループとpush_backの呼び出しが悪いのは、配列が十分に長い場合、ベクターに何度もサイズ変更を強制する可能性があるためです。
bradtgmurray 2008年

@bradtgmurray:上記で提案した「2つのイテレーター」ベクターコンストラクターの適切な実装では、最初に2つのイテレーターでstd :: distance()を呼び出して必要な数の要素を取得し、その後1回だけ割り当てます。
ドリューホール

4
@bradtgmurray:ベクトルの指数関数的な増加(別名 "一定の償却時間")のため、push_back()でもそれほど悪くはありません。最悪の場合、実行時間は2倍程度になると思います。
Drew Hall、

2
そして、ベクターが既に存在する場合、vec.clear(); vec.insert(vec.begin()、a、a + n); 同様に動作します。そうすれば、ポインタをイテレータにするだけで済み、ベクトルの割り当ては一般的に失敗します(そしてC ++ / STLの方法)。
MP24

6
構築できない場合のもう1つの代替方法は、assign:ですvec.assign(a, a+n)。これは、コピーとサイズ変更よりもコンパクトです。
mMontu 2013年

209

ここには多くの答えがあり、それらのほぼすべてが仕事を成し遂げるでしょう。

ただし、誤解を招くアドバイスがあります。

オプションは次のとおりです。

vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}

長い話を短くカットするには、vector :: insertを使用する方法4がbsruthのシナリオに最適です。

ここにいくつかの悲惨な詳細があります:

方法1はおそらく理解するのが最も簡単です。配列から各要素をコピーして、ベクターの後ろにプッシュするだけです。悲しいかな、それは遅いです。ループがあるため(コピー機能に含まれる)、各要素を個別に処理する必要があります。配列とベクトルが連続したブロックであることを知っているという事実に基づいて、パフォーマンスを向上させることはできません。

方法2は、方法1に対して推奨されるパフォーマンス改善です。追加する前に、アレイのサイズを事前に予約してください。大規模な配列の場合、これ役立つ場合があります。ただし、プロファイリングで改善が得られる可能性がある場合(または、イテレーターが無効にならないようにする必要がある場合)にプロファイリングが提案しない限り、ここでの最善のアドバイスは予約を使用しないことです。 Bjarneも同意する。ちなみに、私はこの方法がほとんどの時間で最も遅いことを発見しましたが、方法1よりも通常大幅に遅い理由を包括的に説明するのに苦労しています...

方法3は古い学校の解決策です-問題にCを投げてください!PODタイプで問題なく高速に動作します。この場合、memcpyはベクターの境界の外で機能し、ベクターにサイズが変更されたことを伝える方法がないため、サイズ変更を呼び出す必要があります。醜いソリューション(バイトコピー!)であることは別にして、これはPODタイプでのみ使用できることを覚えておいてください。私はこのソリューションを決して使用しません。

方法4が最善の方法です。それは意味が明確であり、それは(通常)最速であり、どのオブジェクトに対しても機能します。このアプリケーションでこのメソッドを使用することの欠点はありません。

方法5は方法4の微調整です-配列をベクトルにコピーしてから追加します。良いオプション-一般的に高速で明確です。

最後に、配列の代わりにベクトルを使用できることを知っていますよね?関数がCスタイルの配列を期待する場合でも、ベクトルを使用できます。

vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

それが誰かを助けることを願っています!


6
「&dataArray [dataArraySize]」を安全かつ移植性のある方法で参照することはできません。これは、過去の終わりのポインタ/イテレータを逆参照しています。代わりに、dataArray + dataArraySizeと言うと、最初に逆参照する必要なくポインターを取得できます。
Drew Hall、

2
@Drew:はい、できます。少なくともCでは可能です。これは、を&expr評価しないように定義されてexprおり、アドレスを計算するだけです。また、最後の要素の1つ後のポインタも完全に有効です。
Roland Illig、2011年

2
方法4を2で試しましたか?つまり、挿入する前にスペースを予約します。データサイズが大きい場合、複数の挿入には複数の再割り当てが必要になるようです。サイズはアプリオリにわかっているため、挿入する前に再割り当てを行うことができます。
Jorge Leitao 2013

2
@MattyTメソッド5のポイントは何ですか?なぜデータの中間コピーを作成するのですか?
ルスラン

2
個人的には、ポインタが自動的に減衰する配列から利益を得たいと思っています。– dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);私にはもっとはっきりと見えます。方法5からも何も得ることができず、コンパイラーがベクトルを再度最適化できなければ、かなり非効率的に見えます。
アコンカグア

37

既存のデータを置き換えるだけの場合は、これを行うことができます

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}

1
理解しやすく、間違いなく最速のソリューションです(それは舞台裏でのただのちょっとした作業です)。
ドンスコット

deta.assignはdata.insertより高速ですか?
ジム


10

私は自分の回答しか編集できないため、質問に対する他の回答から複合回答を作成します。答えてくださった皆様、ありがとうございました。

std :: copyを使用しても、これはバックグラウンドで繰り返されますが、コードを入力する必要はありません。

int foo(int* data, int size)
{
   static std::vector<int> my_data; //normally a class variable
   std::copy(data, data + size, std::back_inserter(my_data));
   return 0;
}

通常のmemcpyを使用します。これはおそらく基本的なデータ型(つまりint)に最適ですが、構造体やクラスのより複雑な配列には使用できません。

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));

このアプローチをお勧めします。
mmocny 2008年

事前にサイズがわかっていて、back_inserterを使用しない場合は、ベクターのサイズを事前に変更する方が効率的です。
luke

my_data.reserve(size)を追加できます
David Nehme

内部的には、これはあなたが避けたいように見えることを正確に行っていることに注意してください。ビットをコピーするのではなく、ループしてpush_back()を呼び出すだけです。コードの入力を避けたいだけだったと思いますか?
mmocny 2008年

1
データをコピーするためにベクターコンストラクターを使用しないのはなぜですか?
マーティンヨーク

3

memcpyは避けてください。本当に必要な場合を除いて、ポインタ操作をいじる理由はありません。また、POD型(intなど)でのみ機能しますが、構築が必要な型を処理している場合は失敗します。


8
あなたは実際に解決策を提案していないので、これはおそらく他の回答の1つに対するコメントであるべきです。
finnw 2013

3
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source

unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

std::vector<int> myvector (dataArraySize );//target

std::copy ( myints, myints+dataArraySize , myvector.begin() );

//myvector now has 1,2,3,...10 :-)

2
このコードスニペットは歓迎されており、多少の助けになるかもしれませんが、これが問題を解決する方法理由の説明含めると、大幅に改善されます。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを忘れないでください!回答を編集して説明を追加し、適用される制限と前提を示してください。
Toby Speight

4
待って、何myints
mavavilj

2

さらに別の答えは、「関数が呼び出される回数がわからない」という人が言ったので、ベクトルの挿入メソッドを使用して、値の配列をベクトルの末尾に追加することができます。

vector<int> x;

void AddValues(int* values, size_t size)
{
   x.insert(x.end(), values, values+size);
}

ベクトルの実装は、イテレータタイプとタイプ自体に基づいて値を挿入する最良の方法を最適化できるはずなので、この方法が好きです。stlの実装についていくぶん返事をしている。

最速の速度を保証する必要があり、タイプがPODタイプであることがわかっている場合は、Thomasの回答でサイズ変更メソッドをお勧めします。

vector<int> x;

void AddValues(int* values, size_t size)
{
   size_t old_size(x.size());
   x.resize(old_size + size, 0);
   memcpy(&x[old_size], values, size * sizeof(int));
}

1

上記のメソッドに加えて、std :: Vector.reserve()、std :: Vector.resize()のいずれかを使用するか、ベクターをサイズに合わせて構築し、ベクターに十分な要素があることを確認する必要がありますデータを保持します。そうでない場合、メモリが破損します。これはstd :: copy()またはmemcpy()のいずれにも当てはまります。

これがvector.push_back()を使用する理由です。ベクトルの終わりを超えて書き込むことはできません。


back_inserterを使用している場合、コピー先のベクターのサイズを事前に予約する必要はありません。back_inserterはpush_back()を実行します。
John Dibling 2008年

0

ベクター内のアイテムの大きさがわかっていると仮定します。

std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));

http://www.cppreference.com/wiki/stl/vector/start


それはstd :: vectorの実装に依存していませんか?
ReaperUnreal 2008年

ひどい!配列を2回、1つは「0」、次に適切な値で埋めます。次のようにしてください:std :: vector <int> myArray(source、source + item_count); コンパイラを信頼してmemcpyを生成してください!
クリスジェファーソン

コンパイラを信頼して__memcpy_int_alignedを生成します。さらに高速になるはずです
MSalters 2008年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.