2012-03-01 4 views
12

だから、std :: map、lambda、stlアルゴリズム(remove_if)に問題があります。実際には、std :: listやstd :: vectorと同じコードがうまくいきます。map、lambda、remove_if

私のテスト例:

#include <map> 
#include <iostream> 
#include <algorithm> 

struct Foo 
{ 
    Foo() : _id(0) {} 
    Foo(int id) : _id(id) 
    { 

    } 

    int _id;  
}; 
typedef std::map<int, Foo> FooMap; 


int main() 
{ 
    FooMap m; 
    for (int i = 0; i < 10; ++i) 
     m[i + 100] = Foo(i); 

    int removeId = 6; 
    // <<< Error here >>> 
    std::remove_if(m.begin(), m.end(), [=](const FooMap::value_type & item) { return item.second._id == removeId ;}); 

    for (auto & item : m) 
     std::cout << item.first << " = " << item.second._id << "\n";  

    return 0; 
} 

エラーメッセージ:

In file included from /usr/include/c++/4.6/utility:71:0, 
       from /usr/include/c++/4.6/algorithm:61, 
       from main.cxx:1: 
/usr/include/c++/4.6/bits/stl_pair.h: In member function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const int, _T2 = Foo, std::pair<_T1, _T2> = std::pair<const int, Foo>]’: 
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: instantiated from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Rb_tree_iterator<std::pair<const int, Foo> >, _Predicate = main()::<lambda(const value_type&)>]’ 
main.cxx:33:114: instantiated from here 
/usr/include/c++/4.6/bits/stl_pair.h:156:2: error: assignment of read-only member ‘std::pair<const int, Foo>::first’ 

私はここで間違っているものを理解していません。だから、私は喜んでそれに関するいくつかのアドバイス/指示を読んでいます。私の目標 - remove_ifなどのstd :: mapとアルゴリズムで新しいラムダスタイルを使用する。

g ++ 4.6、-std = C++ 0x。

+2

'remove_if'はイテレータのペアを受け取り、イテレータを返します。あなたはどこから** **の要素を削除すると思いますか? –

答えて

27

std::map<K,V>::value_typestd::pair<const K, V>であり、別名.firstconstであり、割り当て不可能です。ラムダはここの問題とは何の関係もありません。

std::remove_ifコンテナの要素を移動することによって項目を「削除」するので、述語に合わないものはすべて、返されたイテレータの前に表示されます。そのイテレータの後のすべてが不特定です。それは単純な代入で行い、const変数に代入することができないので、そのエラーが発生します。

†名前removeは少し誤解を招くことができ、この場合には、あなたが本当にerase_ifをしたいが、残念ながら、それは存在しません。あなたはすべての項目を反復処理し、map.erase(iterator)で手でそれらを消去するに間に合わせなければならない:

for(auto it = map.begin(), ite = map.end(); it != ite;) 
{ 
    if(it->second._id == remove_id) 
    it = map.erase(it); 
    else 
    ++it; 
} 

あなたは他のイテレータが無効になっなしツリー内の個々のノードを消去することができますので、これは安全です。 forループヘッダ自体のイテレータをインクリメントしなかったのは、ノードを消去した場合に要素をスキップするためです。


†は今では、あなたは、このキーがconstである理由がある、std::mapの順序で大混乱をもたらすであろうことに気づいている必要があります - あなたは後にどのような方法で発注に影響を与えることはできませんアイテムが挿入されました。

+0

ありがとうございました。したがって、醜いコードなしでアイテムをstd :: mapから削除するには、エレガントな方法があります: 'void removeFromMap(FooMap&m、int id) { \t for(auto it = m.begin()、end = m)エンド();それ= END;!++ IT)\t \t {\t \t(IT-> second._id == ID)\t \t \t { \t \t \tメートル場合。消去する。 \t \t \t break; \t \t \t} } – Reddy

+0

@Reddy:他の方法はありません。 Btw、IDが一意でない場合は、マップの最初の要素のみを消去します。もしそうであれば、そのループは正常です。 – Xeo

+0

ええ、私はそれについて知っています。 – Reddy

3

マップの検索と消去を使用できます。それはremove_ifほど便利ではありませんが、あなたが得た最高のものかもしれません。

int removeId = 6; 
auto foundIter = m.find(removeId); 

// if removeId is not found you will get an error when you try to erase m.end() 
if(foundIter != m.end()) 
{ 
    m.erase(foundIter); 
}