在循环

时间:2016-07-19 21:29:05

标签: c++ c++11 unordered-map

StackOverflow上有几个答案表明以下循环是从std::unordered_map中删除满足某个谓词pred的元素的好方法:

std::unordered_map<...> m;
auto it = m.begin();
while (it != m.end())
{
    if (pred(*it))
        it = m.erase(it);
    else
        ++it;
}

我对C ++ 11特别感兴趣(而不是C ++ 14),以下不祥的note on cppreference.com表明上述循环依赖于未定义的行为,可能无法在C +中运行毕竟+11:

  

保留未被删除元素的顺序(这样可以在迭代容器时删除单个元素)(自C ++ 14开始)

另请参阅Title 2356. Stability of erasure in unordered associative containers,其中包含对第754页Working Draft N3797第14项请求的措辞更改(附加短语开头&#34;,并保留相对顺序......&#34;)

  

这个措辞与N3797有关。

     

按照指示修改[unord.req],p14:

     

-14- insert和emplace成员不应影响对容器元素的引用的有效性,但可能使所有迭代器无效   容器。擦除成员只能使迭代器和   对擦除元素的引用,并保留相对的顺序   未被删除的元素。

如果我对cppreference.com的注释的解释是正确的,并且上面的循环依赖于C ++ 11中未定义的行为,那么在C ++ 11中解决这个问题的最有效方法是什么? / p>

4 个答案:

答案 0 :(得分:14)

为了遵守C ++ 11,您很可能会对如何解决这个问题有所限制。您的选择基本上归结为:

  1. 迭代unordered_map并构建一个要删除的键列表,如下所示:

    //std::unordered_map<...> mymap;
    std::vector<decltype(mymap)::key_type> vec;
    for (auto&& i : mymap)
        if (/*compare i*/)
            vec.emplace_back(i.first);
    for (auto&& key : vec)
        mymap.erase(key);
    
  2. 迭代对象并重置如果我们找到要删除的内容 - 我真的只推荐这个小数据集。那些觉得goto的人是无条件的坏人,嗯,这个选择可以说是糟糕的。

    //std::unordered_map<...> mymap;
    reset:
    for (auto&& i : mymap)
        if (/*compare i*/) {
            mymap.erase(i.first);
            goto reset;
        }
    
  3. 作为一个 out 选项,你也可以创建一个新的unordered_map并移动你想要保留的元素。当您有更多要删除而不是保留时,这可以说是一个不错的选择。

    //std::unordered_map<...> mymap;
    decltype(mymap) newmap;
    for (auto&& i : mymap)
        if (/*i is an element we want*/)
            newmap.emplace(std::move(i));
    mymap.swap(newmap);
    

答案 1 :(得分:1)

嗯,你可以随时这样做:

std::unordered_map<...> m;
std::vector<key_type> needs_removing;
for(auto&& pair : m)
{
    if (pred(pair))
        needs_removing.push_back(pair.first);
}
for(auto&& key : needs_removing)
    m.erase(key);

速度较慢,但​​复杂程度相同。

答案 2 :(得分:1)

使用 erase_if (c ++ 20)而不是循环(请参见https://en.cppreference.com/w/cpp/container/unordered_map/erase_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;
});

答案 3 :(得分:-3)

首先参考stl-algorithms

这个似乎是想要的: http://www.cplusplus.com/reference/algorithm/remove_if/

概述: http://www.cplusplus.com/reference/algorithm/

修改 cppreference在网站底部有一个类似的例子。 它适用于c ++ 11编译器。