15

所以,我对 std::map、lambda 和 stl 算法(remove_if)有疑问。实际上,与 std::list 或 std::vector 相同的代码效果很好。

我的测试示例:

#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’

我不明白这里有什么问题。所以,我很高兴阅读一些关于它的建议/指导。我的目标 - 使用带有 std::map 和算法的新 lambda 样式,例如 remove_if。

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

4

2 回答 2

30

The problem is that std::map<K,V>::value_type is std::pair<const K, V>, aka .first is const and not assignable. Lambdas have nothing to do with the problem here.

std::remove_if "removes" items by moving the elements of the container around, so that everything that does not fit the predicate is at the front, before the returned iterator. Everything after that iterator is unspecified. It does that with simple assignment, and since you can't assign to a const variable, you get that error.†</sup>

这个名字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' 的排序造成严重破坏,这就是 key 的原因const- 因此在插入项目后您不能以任何方式影响排序。

于 2012-03-01T11:35:16.183 回答
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);
}
于 2013-12-11T17:06:06.203 回答