TL;DR;
std::vectorのinsert()
やerase()
はイテレータを返すし、連続してそのvectorに操作する場合そのイテレータを戻り値で受け取らないのはバグの温床になりがちなので気をつけましょう。
初めに
std::vectorは適切に使えば配列のサイズやらリサイズやらを意識の外に追いやれるので大変便利です。
vector.insert(iter, value)
を使うと、iter
が指す場所に新たにvalue
を挿入し、その後ろの値をいい感じにずらしてくれます。
vector.erase(iter)
を使うと iter
が指している場所の値を消去し、配列のその後ろの値をずらして隙間を埋めてくれます。
どっちも手動で実装すると下らないミスをしがちなのでvector
のメソッドを頼りましょう。
insert
もerase
も、操作をした瞬間に使用したiteratorが指す場所は不定になるのでそのまま使用しては行けません。
正しい使い方はこんな感じ。
[9, 8, 7, 6]
という配列の3
番目の位置に、[0, 1, 2, 3, 4]
を挿入したいという時
#include <vector>
#include <iostream>
int main() {
std::vector<int> a = {9, 8, 7, 6};
auto it = a.begin();
++it;++it; // これでitは a[3] == 7を指す
// ここが本題
for (int i = 0; i < 5; ++i) {
it = a.insert(it, i);
it++; // ここでインクリメントしないと同じ位置に挿入し続けてしまう
}
// 以下デバッグ出力
for (int i = 0; i < a.size(); ++i) {
if (0 < i) std::cout << ", "; // このidiomは綺麗に間にコンマが入るから好き
std::cout << a[i];
}
std::cout << std::endl;
}
9, 8, 0, 1, 2, 3, 4, 7, 6
なお、ループ中の it++;
を無くすと
9, 8, 4, 3, 2, 1, 0, 7, 6
という感じに逆順になる。
一旦受けてその値を即インクリメント、というのは知らないと直感に反するので手に馴染ませましょう。(充分直感的ではあるけれど)
ダメな例
for (int i = 0; i < 5; ++i) {
it = a.insert(++it, i);
}
使う前にインクリメントしてやればいいじゃんとか思ってこんなふうに書くと、itの位置が1つだけずれるがおよそ同じような結果が得られる。
9, 8, 7, 0, 1, 2, 3, 4, 6
しかし、この記法ではvectorの長さが0の時にSEGVする。
長さ0
の配列の[1]
に触ろうとしてしまうのだから当然といえば当然である。