C ++、ベクターにコピーセット


146

にコピーstd::setする必要がありますstd::vector

std::set <double> input;
input.insert(5);
input.insert(6);

std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable

問題はどこだ?


5
また、そこにあるassign()機能は:output.assign(input.begin(), input.end());
ジーンBushuyev

あなたのベクトルは空です。人々が下で指摘しているようにそれを改善する方法はたくさんあります。
AJG85、2011

@Gene:assign()は必要な量のストレージを事前に予約()したいと考えています。イテレータが厳密にInputIteratorでない限り、入力イテレータを使用して必要な量を決定します。その場合、イテレータは予約をスキップし、すべてのpush_back()で再割り当てが行われます。スペクトルの反対側では、BiderectionalIteratorsを使用すると、単にendを減算できます。std :: setのイテレータはどちらでもありません(ForwardIteratorです)。これは残念です。この場合、assign()はセット全体を調べてサイズを決定します。大きなセットではパフォーマンスが低下します。
セルゲイシェフチェンコ2015

回答:


213

を使用する必要がありますback_inserter

std::copy(input.begin(), input.end(), std::back_inserter(output));

std::copy挿入先のコンテナに要素を追加しません。追加できません。コンテナへのイテレータしかない。このため、出力イテレータをに直接渡す場合std::copyは、少なくとも入力範囲を保持するのに十分な大きさの範囲を指すようにする必要があります。

std::back_inserterpush_back各要素のコンテナを呼び出す出力イテレータを作成し、各要素がコンテナに挿入されるようにします。あるいは、std::vectorコピーされる範囲を保持するのに十分な数の要素を作成することもできます。

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

または、std::vector範囲コンストラクターを使用することもできます。

std::vector<double> output(input.begin(), input.end()); 

3
こんにちはジェームズ、あなたのstd :: copy行(あなたの答えの最初のコードブロック)のoutput.insert(output.end(), input.begin(), input.end());代わりに、私は代わりにただ行うことができませんでしたか?
user2015453 2013

または単にcbeginとcendのバージョンを使用する:output.insert(output.cend(), input.cbegin(), input.cend());どう思いますか?ありがとう。
user2015453

2
output.reserve(input.size());を実行する必要があります 自分で、または一部のコンパイラが私のためにそれをすることを願っていますか?
jimifiki 14年

@jimifiki、私は恐れていません。
Alexis Wilke

最初のベクトルの初期化が正しくありません。input,size()空のエントリの配列を作成し、その後に追加を追加します。使うつもりだと思いますstd::vector<double> output; output.reserve(input.size()); std::copy(...);
Alexis Wilke

121

イテレータをとるベクトルのコンストラクタを使用するだけです。

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

vのsのコンテンツだけが必要で、データをコピーする前はvには何もないものと想定します。


42

ここに別の代替方法がありvector::assignます:

theVector.assign(theSet.begin(), theSet.end());

24

セットの内容を保持するのに十分なスペースがベクターオブジェクトに予約されていません。

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

1
これは-1に値しません。特に、これにより、vectorは1つの割り当てのみを実行できます(O(1)のセット反復子の距離を決定できないため)。また、構築時に各要素を0にするためにvectorが定義されていない場合、これにより、コピーをmemcpyに要約できるようにする価値があります。実装がベクトルのctorのループを削除できることがわかった場合、後者はまだ価値があります。もちろん、前者も控えめに達成することができます。
Fred Nurk、2011

知りません。そのお手伝いをさせてください。
ヴィルヘルムテル、2011

私はあなたに-1を与えましたが、それは私の側の思考です。投票を元に戻すことができるように少し編集してください。+ 1を差し上げます。これは、フェイルファーストプロパティのため、実際には非常にクリーンなソリューションです。
Fred Foo

私が答えを自分で編集すれば、賛成投票できることを理解しただけです。それで、フェイルファーストメモリ割り当てに+1が与えられました。ごめんなさい!
Fred Foo

3

最も効率的な方法は、要素を事前に割り当ててから配置することだと思います。

template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)
{
    std::vector<T> to;
    to.reserve(from.size());

    for (auto const& value : from)
        to.emplace_back(value);

    return to;
}

この方法では、最初に既定のコンストラクターを呼び出してから、上記の他のソリューションの代入演算子をコピーするのではなく、すべての要素に対してコピーコンストラクターのみを呼び出します。以下の詳細説明。

  1. back_inserterを使用することもできますが、ベクター(https://en.cppreference.com/w/cpp/iterator/back_insert_iterator)でpush_back()を呼び出しますemplace_back()は、push_back()の使用時に一時ファイルを作成しないため、より効率的です。これは、自明に構築された型の問題ではありませんが、自明でない構築型(たとえば、std :: string)のパフォーマンスに影響します。

  2. すべての要素をデフォルトで構築する(何もしない)サイズ引数を使用してベクトルを構築しないようにする必要があります。たとえば、std :: copy()を使用するソリューションと同様です。

  3. そして、最後に、vector :: assign()メソッドまたはイテレーター範囲を取るコンストラクターは、セットイテレーターでstd :: distance()(要素の数を知るため)を呼び出すため、適切なオプションではありません。セットはバイナリ検索ツリーのデータ構造であり、ランダムアクセス反復子を実装していないため、すべてのセット要素で不要な追加の反復が発生します。

お役に立てば幸いです。


なぜこれが速いのか、なぜaをback_inserter使用する必要がないのかなどの機関への参照を追加してください
Tarick Welling

回答に説明を追加しました。
dshvets1

1

std::copy空のコンテナへの挿入には使用できません。そのためには、次のようにinsert_iteratorを使用する必要があります。

std::set<double> input;
input.insert(5);
input.insert(6);

std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin())); 

3
これは、最初にベクターが再割り当てされるときに失敗します。output.begin()からのイテレーターは無効になります。
Fred Nurk、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.