イテレート中の要素削除

STLの話です。リストの要素をイテレートしながら要素を削除したいときがありますが、そういう場合はこういう風に書きたいわけです。

for ( list<GEAnimation *>::iterator it = m_Animations.begin(); it != m_Animations.end(); it++ ) {
	if ( (*it)->step( this ) ) {
		delete *it;
		m_Animations.remove( *it );
	}
}

gccでは動くのですが、MSのSTLでは動いてくれないので、こういう風に書く必要があります。

list<GEAnimation *>::iterator it = m_Animations.begin();
while ( it !=  m_Animations.end() ) {
	if ( (*it)->step( this ) ) {
		delete *it;
		it = m_Animations.erase( it );
	} else {
		it++;
	}
}

forを使った書き方をよく使うので、そっちで動くように融通がきいてくれればありがたいんですけどね。。。どんな風に書くのがいいんでしょうね。

コメント

  1. remove_ifとeraseを使うのが定石だと思います。ループの中身が別の関数へ追い出されるのが残念と言えば残念ですが。
    bool animation_step_and_delete(GEAnimation* p, hoge* pThis)
    {
    if ( p->step(pThis) )
    {
    delete p;
    return true; //削除する
    }
    return false; //削除しない
    }

    m_Animations.erase(std::remove_if(m_Animations.begin(), m_Animations.end(), boost::bind(animation_step_and_delete, _1, this)), m_Animations.end());
    中のboost::bind(~)の部分は、標準のライブラリだけでstd::bind2nd(std::ptr_fun(animation_step_and_delete), this)とも書けます(こっちの書き方覚えていないんですよ)。

  2. tak より:

    これは for の話題かと思いきや、じつは remove メンバ関数の挙動が違うよー、という話なのですね。

    GCC のほうですが、remove() したあとの it はなぜ依然として次をたぐれるのでしょうね?
    もうリストには繋がれていないので it++ できないはずなのですが。
    これがVC++で iterator を for に組み込めない理由だと思います。

    ほかの突っ込み所として、
    ・remove() は全要素を走査するので単一要素削除にはコストが高い。また要素に重複があると使えない。erase() しませう。
    ・iterator を扱うときは前置の ++it, –it のほうがよさげ?(最適化されるだろうから別にいいかな〜?)

    僕は STL を普段使わないので、知識不足からくる間違いがあるかもしれません。悪しからず。

  3. OverTaker より:

    今更ですが、前者でremove使う意味がわかりませんね。erase 使えばいいものを。erase にしたら、辿れるわけでもありませんが…

    やはり、remove_ifとeraseの方法が定石のようですね。個人的には、remove_if に渡す関数が、真偽判定を超えた機能を持つと、なんとなく気持ち悪いので、今回の場合は erase を使いそうです。( p->step には、アニメーションを進める役割があるので )

  4. foo より:

    解決済みみたいですが、いちおうこれで動くんじゃないかというコード書いときます
    間違ってたらすみませんが消しといてください

    for ( list::iterator it = m_Animations.begin(); it != m_Animations.end(); it++ ) {
     if ( (*it)->step( this ) ) {
      delete *it;
      it = m_Animations.erase( it );
     }
    }

コメント投稿