STLのコンテナの種類によって、要素の削除の仕方もいろいろ。
vectorの場合
erase-remove idiomと呼ばれるテクニックを使う。このテクニックでは、特定の値に一致する要素を消すときにはerase()とremove()とを、また特定の条件に一致する要素を消すときにはerase()とremove_if()とを組み合わせて使う。
#include <iostream> #include <vector> #include <algorithm> using namespace std; // 値が偶数の時にtrueを返す叙述関数 bool is_even(int val){ return !(val % 2); } // mapの要素を順番に表示 void printVector( vector<int> &vec ){ cout << "size = " << vec.size() << endl; for(int i = 0; i < vec.size(); ++i){ cout << vec[i] << ", "; } cout << endl << endl; } int main(void){ vector<int> vec; for(int i = 0; i < 10; ++i){ vec.push_back(i % 5); } printVector(vec); // 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, // 3に等しい要素をすべて削除 vec.erase(remove(vec.begin(), vec.end(), 3), vec.end()); printVector(vec); // 0, 1, 2, 4, 0, 1, 2, 4, // 偶数の要素をすべて削除 vec.erase(remove_if(vec.begin(), vec.end(), is_even), vec.end()); printVector(vec); // 1, 1, return 0; }
mapの場合
erase()がメンバ関数で提供されているのでそれを使う。
#include <iostream> #include <map> #include <algorithm> using namespace std; // vectorの要素を順番に表示 void printMap(map<int, int> &m){ cout << "size = " << m.size() << endl; for(map<int, int>::iterator it = m.begin(); it != m.end(); ++it){ cout << it->first << ", "; } cout << endl << endl; } int main(void){ map<int, int> m; for(int i = 0; i < 10; ++i){ m[i] = (i * 10); } printMap(m); // keyが3の要素を削除 m.erase(3); printMap(m); return 0; }
vectorの各要素をループで走査しながら削除する場合
vectorの各要素をイテレータを使って走査しつつ、条件に合う要素があればログなどを出力しつつその要素を削除したくなるときがある。そのときはerase()の戻り値をイテレータで受けるという、まず自力では思いつきそうもない方法を使う。
#include <iostream> #include <vector> #include <algorithm> using namespace std; void printVector( vector<int> &vec ){ cout << "size = " << vec.size() << endl; for(int i = 0; i < vec.size(); ++i){ cout << vec[i] << ", "; } cout << endl << endl; } int main(void){ vector<int> vec; for(int i = 0; i < 10; ++i){ vec.push_back(i % 5); } printVector(vec); // 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, // forループの3つ目の式はあえて空 for(vector<int>::iterator it = vec.begin(); it != vec.end(); ){ if (*it == 3){ // 削除条件に合う要素が見つかった cout << "val " << *it << " is deleted." << endl; it = vec.erase(it); // erase()の戻り値をitで受ける! } else{ ++it; } } printVector(vec); // 0, 1, 2, 4, 0, 1, 2, 4, return 0; }
mapの各要素をループで走査しながら削除する場合
こちらの場合はメンバ関数のerase()が戻り値を戻さないので、少しvectorの場合と形が違う。こちらもトリッキー。
#include <iostream> #include <map> #include <algorithm> using namespace std; void printMap(map<int, int> &m){ cout << "size = " << m.size() << endl; for(map<int, int>::iterator it = m.begin(); it != m.end(); ++it){ cout << it->first << ", "; } cout << endl << endl; } int main(void){ map<int, int> m; for(int i = 0; i < 10; ++i){ m[i] = (i * 10); } printMap(m); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, for(map<int, int>::iterator it = m.begin(); it != m.end(); ){ if (it->first == 9){ // 削除条件に合う要素が見つかった cout << "key " << it->first << " is deleted." << endl; m.erase(it++); } else{ ++it; } } printMap(m); // 0, 1, 2, 3, 4, 5, 6, 7, 8, return 0; }
STLはやはり難しい…
参考
- erase-remove idiomでいったい何が起こっているのかがよくわかる。
- ループを回しながら要素を削除する方法について、一つずつ落とし穴にはまりながらの解説。
- 定番の解説書 Effective STL―STLを効果的に使いこなす50の鉄則 の第9項。