11

この質問に触発され、ベクトルをそれ自体に追加する方法を尋ねると、私の最初の考えは次のとおりでした(そして、はい、今insertではより良いオプションであることに気づきました):

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

int main() {
    std::vector<int> vec {1, 2, 3};
    std::copy (std::begin (vec), std::end (vec), std::back_inserter (vec));

    for (const auto &v : vec)
        std::cout << v << ' ';
}

ただし、これは次のように出力します。

1 2 3 1 * 3

* は、プログラムが実行されるたびに異なる番号です。2つしか入れ替わっていないのが異様で、実際にその説明があれば聞きたいです。続けて、別のベクトル (元のコピー) に追加すると、正しく出力されます。次の行を前に追加すると、正しく出力されますcopy

vec.reserve (2 * vec.size());

std::back_inserter事前にメモリを予約していなくても、コンテナーの最後に要素を追加する安全な方法であるという印象を受けました。私の理解が正しければ、コピー行の何が問題になっていますか?

コンパイラとは関係ないと思いますが、GCC 4.7.1 を使用しています。

4

1 に答える 1

12

std::back_inserter要素をコンテナに挿入する挿入イテレータを作成します。この反復子が逆参照されるたびに、コンテナーを呼び出しpush_backて新しい要素をコンテナーに追加します。

コンテナーの場合、 whereをstd::vector呼び出すと再割り当てが行われます。ベクターの内容を格納するために新しい配列が作成され、その現在の内容が新しい配列にコピーされ、古い配列が破棄されます。この時点でのベクトルへのイテレータはすべて無効になり、使用できなくなります。push_backv.size() == v.capacity()

begin(vec)プログラムでは、これには、アルゴリズムによって定義され、end(vec)そこからcopyアルゴリズムがコピーされる入力範囲が含まれます。これらの反復子が無効化されても、アルゴリズムは引き続きこれらの反復子を使用するため、プログラムは未定義の動作を示します。


コンテナーに十分な容量があったとしても、その動作は未定義です。仕様では、挿入時に「再割り当てが発生しない場合、挿入ポイントの前のすべての反復子と参照は有効なままです」と記載されています (C++11 §23.3.6.5 /1)。

への呼び出しpush_backは末尾への挿入と同等であるため、std::end(vec)渡した終了イテレータ () は へstd::copyの 1 回の呼び出しの後で無効になりpush_backます。入力範囲が空でない場合、プログラムは未定義の動作を示します。


std::deque<int>aまたは aを使用した場合、プログラムの動作は明確に定義されることに注意してくださいstd::list<int>。要素が追加されたときにこれらのコンテナのどちらも反復子を無効にしないためです。

于 2012-07-16T19:55:15.160 に答える