俺がstd::removeを理解できないのはどう考えても仕様が悪い。
というわけで、std::removeの挙動の再確認。
std::removeは、第一引数から第二引数までのコンテナ内の、第三引数で指定された値を削除する(嘘)、STLの関数だ。
今回使うのは以下。removeのためにalgorithm。iteratorはstd::beginを気分で使う。
#include <vector> #include <iostream> #include <algorithm> #include <iterator>
まず配列を用意して初期化する。
std::vector<int> vec = { 1, 2, 3, 2 , 4, 2 , 5 }; for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
2
3
2
4
2
5
次に、この配列の値2に対してstd::removeをかける
auto end = std::remove(std::begin(vec), std::end(vec), 2); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
3
4
5
4
2
5
さあ、もう訳がわからない。消えたはずの2は残っていてその割に個数は減っていてその上位置まで変わっていて、さらにいじったつもりのない4と5が増えている。
Erase-Removeエディオムというのがあり、eraseと併用すれば消える。
vec.erase( std::remove( std::begin(vec), std::end(vec), 2 ), std::end(vec) ); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
これはつまりこういうことだ。
std::removeは、指定した削除対象の値以外のものを配列の左側に再配置する。
そして戻り値として、必要なデータが入っている領域の次の値へのイテレータを返す。
std::removeが行うのはコンテナの中の値の書き換えだけなので、高速だが、そのままではいわば「破棄推奨領域」が生じてしまう。
(※) end2 = std::remove(begin,end,value);というようにして、以後の処理を begin~end2の範囲に対して行うようにすれば、消えたものとして処理できる。
この破棄推奨領域を削除する行為は、確保されているメモリ領域の変更なので、std::eraseを用いる必要がある。