中身がポインタのvectorから特定要素を削除

vectorから特定の条件でフィルターをかけて要素を削除するにはerase-removeというイディオムがある。std::remove_ifで条件に当てはまる要素を後ろに移動させ、返り値のiteratorから元のvectorの最後尾までをeraseにかける、というもの。

cf. std::remove_ifは実際には要素の削除を行わない。

これを要素がポインタのvectorに対してやってみたところうまくいかなかった。挙動を見ると、条件に当てはまらない(=残したい)要素でも削除されてしまうものが存在するようだった。

ググってみるとこんなのがstackoverflowに見つかった

c++ - Use std::remove_if on a std::vector filled with pointers - Stack Overflow

remove_ifではなくpartitionを使えといっている。 ということでpartitionを調べてみると、条件にあう要素を前方に、合わない要素を後方に移動させ、2つ目のグループの最初のiteratorを返す、というものらしい。

remove_ifと何が違うのか?と思ったがこういうのも上がっていた

Difference between partition() and remove() functions in C++ - Stack Overflow

どうやらremove_ifは条件に当てはまる不要な要素を後方に集めるという処理はしないものだった。なので結論として、remove_ifの仕様を勘違いしていることが原因だった。

remove_ifのreferenceを見ると

cpprefjp.github.io

条件に当てはまらない要素(有効な要素)を前方に集めると書いてあるが、条件に当てはまる(不要な要素)を後ろに移動させるとは書いていない。

よってポインタを要素とするvectorから特定の要素を削除したい場合はstd::partitionを使う必要がある。例えばこんな感じ

struct A {
    int id;
    A(id): id(id) {}
}

auto v = {new A(1), new A(2), new A(3)}

auto it = std::partition(v.begin(), v.end(), [](A *a) {
    return a.id == 2;
});

std::for_each(v.begin(), it, [](A *a) { delete a; });
v.erase(v.begin(), it);