2012-03-01 66 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’ 

我不明白什麼是錯在這裏。所以,我很樂意閱讀一些關於它的建議/方向。我的目標 - 使用新的lambda樣式與std :: map和算法,如remove_if。

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

+2

'remove_if'接受一對迭代器,並返回一個迭代器。你認爲它從**中刪除了元素**? – 2012-03-01 11:35:17

回答

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循環頭本身增加迭代器,因爲在刪除節點的情況下,它會跳過一個元素。


†現在,你應該已經注意到,這將肆虐的std::map的排序,這就是爲什麼,關鍵是const的原因 - 這樣後,你不能影響排序以任何方式項目已插入。

+0

謝謝你的回答。所以,有沒有優雅的方式從std :: map中刪除項目,而沒有醜陋的代碼: 'void removeFromMap(FooMap&m,int id) { for(auto it = m.begin(),end = m。端();!它=端; ++它)\t \t { \t \t如果(它 - > second._id == ID)\t \t {\t \t \t \t微米。擦除(它); \t \t \t break; \t \t} \t} }' – Reddy 2012-03-01 11:44:51

+0

@Reddy:沒有,沒有其他方式我知道。順便說一句,如果你的ID是非唯一的,你只會擦除地圖中的第一個元素。如果他們是,那麼這個循環是好的。 – Xeo 2012-03-01 11:46:45

+0

是的,我知道它。 – Reddy 2012-03-01 11:52:18

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); 
}