简体   繁体   English

C++ 擦除映射中的键值,同时迭代它

[英]C++ Erase key-value in map while iterating over it

The following C++ code seems to work, and I'm curious to learn how:以下 C++ 代码似乎有效,我很想了解如何:

    std::map<char,char> mymap;
    mymap['a'] = 'A'; mymap['b'] = 'B'; mymap['c'] = 'C';
    mymap['d'] = 'D'; mymap['e'] = 'E'; mymap['f'] = 'F';
    mymap['g'] = 'G'; mymap['h'] = 'H'; mymap['i'] = 'I';

    bool erase = true;
    for (auto& [key, value] : mymap) {
        if (erase) mymap.erase(key); // Erasing every other item!!
        erase = !erase;
    }

I'm modifying the map while iterating over it.我在迭代时修改地图。 Coming from the JVM world I was expecting a CuncurrentModificationException of sorts... but it seems to work fine in C++.来自 JVM 世界,我期待各种CuncurrentModificationException ......但它似乎在 C++ 中工作正常。

Is this actually breaking and I'm just getting lucky?这真的打破了吗,我只是走运了? If not, how does this work under the hood?如果没有,这在幕后如何工作?

Most examples of doing this use iterators instead, I'm guessing there's a reason for that?大多数这样做的例子都使用迭代器,我猜这是有原因的吗? This way look cleaner to me, I doubt someone would pick the more verbose and less legible for (auto& it = mymap::begin(); it != mymap::end; ) { it++ } approach without a good reason for it.这种方式对我来说看起来更干净,我怀疑有人会在没有充分理由的情况下for (auto& it = mymap::begin(); it != mymap::end; ) { it++ }选择更冗长且不易辨认的方法。

C++ std uses narrow contracts to permit programs to be faster. C++ std使用窄契约来允许程序更快。

A narrow contract is one where not every operation has a guaranteed result.狭义合约是一种并非每个操作都有保证结果的合约。 If you violate the terms of the contract, C++ places no guarantees on the behaviour of your program.如果您违反了合同条款,C++ 不保证您的程序的行为。

In this case, you destroy an element while using an iterator referring to it to iterate.在这种情况下,您在使用引用它的迭代器进行迭代时销毁元素。 This invalidates the iterator, and the implicit ++ advance operation in the for(;) loop then violates your contract with the std library.这会使迭代器无效,并且for(;)循环中的隐式++ Advance 操作违反了您与std库的合同。

With a wide contract, you'd get something like an exception here.有了一个宽合同,你会在这里得到一个例外。 With a weak contract, you get undefined behaviour.使用弱合同,您会得到未定义的行为。 Sometimes it "works", sometimes it crashes, sometimes it emails your browser history to your grandmother and deletes your gmail account.有时它“有效”,有时它崩溃,有时它会将您的浏览器历史记录通过电子邮件发送给您的祖母并删除您的 Gmail 帐户。

All are valid responses to violating the contract.所有这些都是对违反合同的有效回应。 The C++ standard places no restrictions on what the executable does. C++ 标准对可执行文件的功能没有任何限制。 This can include time travel (and no, this one isn't a joke; UB permits the compiler to change what it did on lines before the program reached the UB).这可以包括时间旅行(不,这不是一个笑话;UB 允许编译器在程序到达 UB 之前更改它在行上所做的事情)。

I personally write a remove_erase_if algorithm to solve this problem once.我个人曾经写过一个remove_erase_if算法来解决这个问题。 And yes, it uses iterators.是的,它使用迭代器。

You mainly have to write something like that if you want to remove every other item:如果您想删除所有其他项目,您主要必须编写类似的内容:

for (auto it = mymap.begin, itEnd = mymap.end(); it != itEnd; ++it)
{
    auto itTemp = it;
    ++it;
    mymap.erase(itTemp);
    if (it == itEnd)
    {
        break;
    }
}

That is, you have to make a copy of the iterator so that you can still have a valid iterator after erase the item.也就是说,您必须制作迭代器的副本,以便在擦除项目后仍然可以拥有一个有效的迭代器。

Note that the above code is correct for a map but would not works for a vector for example.请注意,上面的代码对于map是正确的,但不适用于vector

This is why you have to read the documentation each time you do an operation that might invalidate iterators, references or pointers to items if you don't know the rules.这就是为什么每次执行可能会使迭代器、引用或指向项的指针(如果您不知道规则)无效的操作时都必须阅读文档的原因。

For a map, see http://www.cplusplus.com/reference/map/map/erase/ .有关地图,请参阅http://www.cplusplus.com/reference/map/map/erase/

Update更新

The first 3 lines of the loop can be replaced in C++ 11 with循环的前 3 行可以在 C++ 11 中替换为

it = mymap.erase(it);

as the never function returns an iterator to next element.因为 never 函数返回一个迭代器到下一个元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM