Move_if 等效于 std: : map

我试图根据特定条件从地图上删除一系列元素。我如何做它使用 STL 算法?

最初我想使用 remove_if,但是这是不可能的,因为 move _ if 不适用于关联容器。

是否有任何“移除 _ 如果”等效算法的地图工程?

作为一个简单的选项,我想通过循环地图和擦除。但是循环遍历地图并删除是一个安全的选择吗?(因为迭代器在擦除后无效)

我举了以下例子:

bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}


int main(void)
{


std::map<int, std::string> aMap;


aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";


//      does not work, an error
//  std::remove_if(aMap.begin(), aMap.end(), predicate);


std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();


for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}


return 0;
}
74548 次浏览

差不多了。

for(; iter != endIter; ) {
if (Some Condition) {
iter = aMap.erase(iter);
} else {
++iter;
}
}

如果您确实从迭代器中删除了一个元素,那么最初的迭代器 两次将会增加; 您可能会跳过需要被删除的元素。

这是一个常见的算法,我已经在很多地方看到使用和记录。

[编辑]你是对的,迭代器在擦除之后失效,但是只有引用被擦除元素的迭代器,其他迭代器仍然有效。因此,在 erase()调用中使用 iter++

我从 优秀的 SGI STL 参考拿到了这份文件:

地图有一个重要的属性 在地图中插入新元素 不会使迭代器无效 指向现有元素 元素从地图也没有 使任何迭代器失效,除非 当然,对于实际上 指向正在被 被抹去了。

因此,指向要擦除的元素的迭代器当然会失效。做这样的事情:

if (some condition)
{
iterator here=iter++;
aMap.erase(here)
}

从下面的注释开始:

Http://www.sgi.com/tech/stl/pairassociativecontainer.html

双关联容器不能提供可变的迭代器(如 TrivialIterator 需求中定义的) ,因为可变的迭代器的值类型必须是可分配的,而双关联容器不是可分配的。但是,双关联容器可以提供不完全常量的迭代器: 迭代器使表达式(* i)。Second = d 是有效的。

恕我直言,没有 remove_if()等价物。
你不能重新排列地图。
所以 remove_if()不能把你感兴趣的对放在你可以调用 erase()的末尾。

首先

Map 有一个重要的属性,即向 Map 中插入新元素不会使指向现有元素的迭代器失效。从映射中擦除一个元素也不会使任何迭代器失效,当然,除了那些实际指向被擦除的元素的迭代器。

第二,下面的代码很好

for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}

调用函数时,在调用该函数之前计算参数。

因此,当在调用擦除之前计算 iter + + 时,迭代器的 + + 操作符将返回当前项,并在调用之后指向下一个项。

如果要擦除键大于2的所有元素,那么最好的方法是

map.erase(map.upper_bound(2), map.end());

但只适用于范围,而不适用于任何谓词。

原始代码只有一个问题:

for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}

在这里,iter在 for 循环中增加一次,在擦除中增加另一次,这可能会以某个无限循环结束。

对于 std: : map (和其他容器)擦除 _ if

我使用下面的模板。

namespace stuff {
template< typename ContainerT, typename PredicateT >
void erase_if( ContainerT& items, const PredicateT& predicate ) {
for( auto it = items.begin(); it != items.end(); ) {
if( predicate(*it) ) it = items.erase(it);
else ++it;
}
}
}

这不会返回任何内容,但会从 std: : map 中删除项。

用法例子:

// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
return /* insert appropriate test */;
});

第二个示例(允许您传入测试值) :

// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4;  // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
return item.property < test_value;  // or whatever appropriate test
});

基于 钢铁救世主的回答对于那些希望提供更多沿着标准函数获取迭代器行的范围的人来说。

template< typename ContainerT, class FwdIt, class Pr >
void erase_if(ContainerT& items, FwdIt it, FwdIt Last, Pr Pred) {
for (; it != Last; ) {
if (Pred(*it)) it = items.erase(it);
else ++it;
}
}

我很好奇是否有办法丢失 ContainerT项并从迭代器中获取它。

现在,std::experimental::erase_if在头 <experimental/map>中可用。

见: http://en.cppreference.com/w/cpp/experimental/map/erase_if

我用这个

 std::map<int, std::string> users;
for(auto it = users.begin(); it <= users.end()) {
if(<condition>){
it = users.erase(it);
} else {
++it;
}
}

这里有一些优雅的解决方案。

for (auto it = map.begin(); it != map.end();)
{
(SomeCondition) ? map.erase(it++) : (++it);
}

对于 C + + 20上的那些程序,< strong > map < strong > unorder _ map 都有内置的 擦除 _ if功能:

std::unordered_map<int, char> data \{\{1, 'a'},{2, 'b'},{3, 'c'},{4, 'd'},
{5, 'e'},{4, 'f'},{5, 'g'},{5, 'g'}};
 

const auto count = std::erase_if(data, [](const auto& item) {
auto const& [key, value] = item;
return (key & 1) == 1;
});