2010-11-23 14 views
13

std :: listから要素を削除することはできますか?例:so:std :: listからiteratingしている要素を削除することはできますか?

std::list<int> lst; 
//.... 
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++) 
{ 
    if (*itr > 10) 
     lst.remove(*itr); 
} 

? なぜですか?

+1

[STLリストの項目を消去する](http://stackoverflow.com/questions/501962/erasing-items-from-an-stl-list) – sth

+0

はい、しかしそうではありません。 –

+0

可能な複製[要素を反復処理しながらstd :: listから削除することはできますか?](http://stackoverflow.com/questions/596162/can-you-remove-elements-from-a-stdlist-while- iterating-through-it) – AShelly

答えて

29

正しいコードは以下の通りです:あなたがリストから項目を削除すると(それが削除されている項目を指している場合。)

for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/) 
{ 
    if (*itr > 10) 
     itr = lst.erase(itr); 
    else 
     ++itr; 
} 

、あなたはイテレータを無効にする可能性があるので、あなたはeraseを使用して削除する必要があります(次の項目を指す有効なイテレータを返します)。

さらに良いアイデアがstd::remove_ifを使用することになります。

bool greater_than_10(int x) 
{ 
    return x > 10; 
} 

lst.remove_if(greater_than_10); 

コンパイラがlambdasをサポートしている場合、あなたも短いそれを置くことができます。

lst.remove_if([](int x){ return x > 10; }); 

(私は私のように、このコードをテストしていませんコンパイラはそれほど新しいものではなく、ラムダ関数は感謝して@ John Diblingの答えから盗まれています)。


実際には、リストから消去するとonly the iterators pointing to the item being deletedが無効になります。ただし、他のSTLコンテナにはこのプロパティはありません。


だから、要するに:それを反復しながら、削除は反復子を無効(およびプログラム意志おそらくクラッシュ)可能性があるため、一般的に言えば、あなたは、リストから項目を削除しないでください。ただし、削除するアイテムが、削除の瞬間に使用したイテレータによって参照される値ではないことを完全に確信している場合は、削除することができます。

他のSTLコンテナ(たとえばベクトル)では、制約がより厳密になることに注意してください。コンテナから削除すると、削除されたアイテムを指すイテレータだけでなく、他のイテレータも無効になります。したがって、それらの反復処理中にそのコンテナから削除することは、さらに問題になります。

+0

ハ。 2人のSO人がほぼ同じサンプルコードを投稿しているときは、良い考えでなければなりません。 – aschepler

+0

@aschelper:確かに;-) – Vlad

+0

ああ、私は悪い例を与えた。私はちょうど削除メソッドを使用したい(私は特定の条件であれば特定の値を持つすべてのアイテムを削除したい)。 –

0

私はできると思いますが、removeの代わりにeraseメソッドで行うことができる要素の削除後にイテレータを再割り当てする必要があります。

それ以外の場合は安全でないため、実行しないでください。

8

いいえ。例コードでは、itrが無効になり、未定義の動作が発生します。しかし、これはうまくいく:

+0

イテレータのプリインクリメントは良い考えです。これに合わせてコードを変更しました。今度は、例のコードはまったく同じです:) – Vlad

3

いいえ、できません。

しかし、あなた(とすべきである)「10 'よりも大きいと言うファンクターと一緒にstd::remove_ifを使用することができ、このように:

#include <list> 
#include <algorithm> 


int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end()); 
} 

もう一つ、これを行うために、より汎用的な方法は、独自のカスタムファンクタを書くことです。ここには、is_a_matchファンクタtrueを返します。チェックされている値が10より大きい場合。あなたがC++ 0xの準拠コンパイラの利点を持っている場合は

#include <list> 
#include <algorithm> 
#include <functional> 

struct is_a_match : public std::unary_function<int, bool> 
{ 
    is_a_match(int val) : val_(val) {}; 
    bool operator()(int victim) const { return victim > val_; } 
private: 
    int val_; 
}; 

int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10))); 
} 

は、あなたもラムダを使用することができ、作る:あなたは、それが「一致」に、あなたのケースであらゆる手段に対応するようにtrueを返すようにoperator()を再定義することができますそれは、可能なファンクタを取り除くためにとイテレータの説明についてはhttp://www.cppreference.com/wiki/iterator/startを参照してください。多くの場合

#include <list> 
#include <algorithm> 

int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;})); 
} 
+0

実際には、 'list'自身の' remove_if'メンバ関数を持っています: 'lst.remove_if(std :: bind2nd(std :: greater ()、10));' –

+0

@ Fred:Trueですが、これはより一般的で、 'list'以外のコレクションでこれを行う方法を教えています。私はScott Meyersとは対照的に、非メンバ関数を好んでおり、一般的な考え方も拡張しています。 –

+0

@ジョン:同意。リストのほうが最適なので、 'list'はこのようなメンバ関数(および' sort'など)を提供すると思います。それはあまりにも消去/イディオムを削除するよりも見た目に見えます。しかし、私はあなたの点を感謝します。 –

0

により表現コードを記述します。

ノートのカップル:あなたの代わりにポストインクリメント演算子の前インクリメント演算子(++itr)を使用する必要があります

  • itr++
  • 無効化は、イテレータの正確な実装に依存し、関連するコレクション
関連する問題