伝えられるところでは、イテレータが無効になるため、反復中にコンテナ内の要素を単に消去/削除することはできません。特定の条件を満たす要素を削除する(安全な)方法は何ですか? stlのみを使用してください。ブーストやtr1は使用しないでください。
[〜#〜] edit [〜#〜]特定の条件を満たす要素の数を消去したい場合、おそらくfunctorとfor_eachまたはeraseアルゴリズムを使用して、よりエレガントな方法はありますか?
bool IsOdd( int i )
{
return (i&1)!=0;
}
int a[] = {1,2,3,4,5};
vector<int> v( a, a + 5 );
v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to<int>(), 4 ) ), v.end() );
// v contains {1,2,3,5}
v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() );
// v contains {2}
イテレータを消去した後でイテレータを無効にしない限り、次のことができます。
MyContainer::iterator it = myContainer.begin();
while(it != myContainer.end())
{
if (*it == matchingValue)
{
myContainer.erase(it++);
}
else
{
++it;
}
}
Std :: vectorの例
#include <vector>
using namespace std;
int main()
{
typedef vector <int> int_vector;
int_vector v(10);
// Fill as: 0,1,2,0,1,2 etc
for (size_t i = 0; i < v.size(); ++i){
v[i] = i % 3;
}
// Remove every element where value == 1
for (int_vector::iterator it = v.begin(); it != v.end(); /* BLANK */){
if (*it == 1){
it = v.erase(it);
} else {
++it;
}
}
}
Viktorのソリューションには、削除する前に要素を使用して何かを実行できるという利点があります。 (私は_remove_if
_または_remove_copy_if
_でこれを行うことができませんでした。)しかし、私は_std::find_if
_を使用することを好むので、イテレーターを自分でインクリメントする必要はありません。
_typedef vector<int> int_vector;
int_vector v;
int_vector::iterator itr = v.begin();
for(;;)
{
itr = std::find_if(itr, v.end(), Predicate(4));
if (itr == v.end())
{
break;
}
// do stuff with *itr here
itr = v.erase(itr); // grab a new, valid iterator
}
_
述語はbind1st( equal_to<int>(), 4 )
または次のようなものである可能性があります:
_struct Predicate : public unary_function<int, bool>
{
int mExpected;
Predicate(int desired) : mExpected(desired) {}
bool operator() (int input)
{
return ( input == mExpected );
}
};
_
1. _std::vector<>
_の場合:
_std::vector <int> vec;
vec.erase(std::remove(vec.begin(),vec.end(), elem_to_remove), vec.end());
_
2. _std::map<>
_の場合は、常にstd::map::erase()
を使用します
_std::map<int,std::string> myMap;
myMap.emplace(std::make_pair(1, "Hello"));
myMap.emplace(std::make_pair(2, "Hi"));
myMap.emplace(std::make_pair(3, "How"));
myMap.erase( 1);//Erase with key
myMap.erase(myMap.begin(), ++myMap.begin() );//Erase with range
for( auto &ele: myMap)
{
if(ele.first ==1)
{
myMap.erase(ele.first);//erase by key
break; //You can't use ele again properly
//wthin this iteration, so break.
}
}
_
std::list
_の場合は、std::list::erase()
を使用しますwhile
のバージョンが好きです:
typedef std::list<some_class_t> list_t;
void f( void ) {
// Remove items from list
list_t::iterator it = sample_list.begin();
while ( it != sample_list.end() ) {
if ( it->condition == true ) {
it = sample_list.erase( it );
} else ++it;
}
}
while
を使用すると、it
ループの場合のようにfor
を2回インクリメントする危険はありません。
markh44は最もSTLっぽい応答です。ただし、一般に、イテレータはコンテナを変更することで無効になりますが、setとmapは例外であることに注意してください。そこで、イテレータが参照しているアイテムそのものを削除する場合を除いて、アイテムを削除し、イテレータを引き続き使用できます。
template <class Container, class Predicate>
void eraseIf( Container& container, Predicate predicate ) {
container.erase( remove_if( container.begin(), container.end(), predicate ), container.end() );
}
// pre-c++11 version
template<class K, class V, class Predicate>
void eraseIf( std::map<K,V>& container, Predicate predicate) {
typename std::map<K,V>::iterator iter = container.begin();
while(iter!=container.end()) {
iterator current = iter++;
if(predicate(*current))
container.erase(current);
}
}
// c++11 version
template<class K, class V, class Predicate>
void eraseIf( std::map<K,V>& container, Predicate predicate) {
auto iter = container.begin();
while(iter!=container.end()) {
if(predicate(*iter))
iter = container.erase(iter);
else
++iter;
}
}
ポストデクリメント演算子がイテレータのコピーを返すという事実を使用してくださいbeforeデクリメントします。デクリメントされたイテレータは現在の要素を消去した後も有効であるため、forループは意図したとおりに動作し続けます。
#include <list>
std::list<int> myList;
for(int i = 0; i < 10; ++i )
{
myList.Push_back(i);
}
int cnt = 0;
for(std::list<int>::iterator iter = myList.begin(); iter != myList.end(); ++iter)
{
if( cnt == 5 )
{
myList.erase(iter--);
}
++cnt;
}
編集:リストの最初の要素を消去しようとすると機能しません..。