LoginSignup
32
13

More than 5 years have passed since last update.

std::vectorのinsertを正しく使う

Last updated at Posted at 2017-03-17

TL;DR;

std::vectorのinsert()erase()はイテレータを返すし、連続してそのvectorに操作する場合そのイテレータを戻り値で受け取らないのはバグの温床になりがちなので気をつけましょう。

初めに

std::vectorは適切に使えば配列のサイズやらリサイズやらを意識の外に追いやれるので大変便利です。
vector.insert(iter, value) を使うと、iterが指す場所に新たにvalueを挿入し、その後ろの値をいい感じにずらしてくれます。
vector.erase(iter) を使うと iterが指している場所の値を消去し、配列のその後ろの値をずらして隙間を埋めてくれます。
どっちも手動で実装すると下らないミスをしがちなのでvectorのメソッドを頼りましょう。

inserteraseも、操作をした瞬間に使用したiteratorが指す場所は不定になるのでそのまま使用しては行けません。

正しい使い方はこんな感じ。
[9, 8, 7, 6]という配列の3番目の位置に、[0, 1, 2, 3, 4] を挿入したいという時

insert.cpp
#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

という感じに逆順になる。
一旦受けてその値を即インクリメント、というのは知らないと直感に反するので手に馴染ませましょう。(充分直感的ではあるけれど)

ダメな例

dame.cpp
  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]に触ろうとしてしまうのだから当然といえば当然である。

32
13
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
13