🍣
【C++】vector自身をstd::back_inserterできない理由
トピック
既存の動的配列があったとして、その配列自身を最後尾に付け加えて、ちょうどもとの繰り返しになるような配列を作る場合を考える。そこで、以下のようなコードを実行する。
int main() {
std::vector<int> vec{ 1, 2, 3 ,4, 5 };
std::copy(std::begin(vec), std::end(vec), std::back_inserter(vec));
for (auto v : vec) std::cout << v << ' ';
return 0;
}
さて、これで期待通りに動作するだろうか。すなわち
1 2 3 4 5 1 2 3 4 5
なる出力結果が得られるだろうか。答えはNoである。私の環境下では
1 2 3 4 5 1 -572662307 -572662307 -572662307 -572662307
となった。原因は一体何だろうか。
原因
結論から言うと、std::back_inserter
の仕様がキモである。std::back_inserter
は、要するに引数となる動的配列に対してstd::push_back
を繰り返し行っている。std::push_back(vec)
が行っているのは、以下の内容だ。
- 引数となる配列よりもサイズが1大きい新しい配列
new_vec
を生成する。 - 元の配列
vec
の各要素をnew_vec
に同じ位置にコピーする - 末尾に追加する要素を
new_vec
の最後のコンテナに代入する。 vec
を破壊する。-
new_vec
をvec
とする。
もう気づいた方もいるのではないだろうか。
std::copy(std::begin(vec), std::end(vec), std::back_inserter(vec));
で参照していくイテレータは下図のようになる。
これがstd::back_inserter
によってどうなるかというと、先ほどの説明により以下のような状態になる。
イテレータの指す場所にある要素は、もう既に破壊されているためundefined
な操作となる。
したがって、最初の要素を除いて正しい値が挿入されないといった現象が発生する。
解決策
シンプルに別の動的配列を定義すればよい。
int main() {
std::vector<int> vec{ 1, 2, 3 ,4, 5 };
std::vector<int> vec1(5);
std::copy(std::begin(vec), std::end(vec), std::begin(vec1));
std::copy(std::begin(vec1), std::end(vec1), std::back_inserter(vec));
for (auto v : vec) std::cout << v << ' ';
return 0;
}
Discussion