用作 std::multimap 擦除范围的第一个和最后一个的递增迭代器

Incrementing iterator that is used as first and last of std::multimap erase range

本文关键字:最后一个 迭代器 第一个 multimap 擦除 范围 用作 std      更新时间:2023-10-16

在运行一个示例来展示如何从 std::map/multimap 中删除范围时,我注意到以下代码中存在奇怪的行为:

#include <map>
#include <iostream>
#include <string>
int main()
{
    std::multimap<int, std::string> myMap;
    myMap.insert(std::make_pair(3, "three1"));
    myMap.insert(std::make_pair(3, "three2"));
    myMap.insert(std::make_pair(3, "three3"));
    myMap.insert(std::make_pair(45, "fourty five"));
    myMap.insert(std::make_pair(-1, "minus one"));
    std::multimap<int, std::string>::iterator iter = myMap.find(3);
    if (iter != myMap.end()) {
        myMap.erase(iter, iter++); //segmentation fault(!)
    }
    for (auto element : myMap) {
        std::cout << element.first << " -> " << element.second << std::endl;
    }
    return 0;
}

我使用命令g++ --std=c++11 main.cpp构建(我使用 g++ 5.2.1)。

为什么迭代器的后递增会导致分段错误?我宁愿说这应该创建此迭代器的 2 个副本,将它们传递到 erase 方法中,像代码一样"不擦除任何内容"myMap.erase(iter, iter);然后递增iter.

这个段错误背后有什么逻辑?

这是对迭代器的无效使用iter吗?如果是这样 - 为什么?

顺便说一句。当我使用预递增myMap.erase(iter, ++iter)时,它会编译,在这里它"不擦除任何内容",如上所述。

未定义函数调用参数的计算顺序。 所以当你写:

   myMap.erase(iter, iter++); //segmentation fault(!)

编译器可以自由地先计算第二个参数,也可以不计算第二个参数。当您使用相同的迭代器但有副作用时,您会得到未定义的行为(请参阅C++标准,第 1.9/15 节)。

例如,如果编译器首先计算第二个参数iter++,则递增的迭代器将用作第一个参数,而第二个参数不会递增iter。结果:传递给 erase() 的范围将是[std::next(iter), iter) - 该函数可能会尝试擦除超出范围的元素(即 UB)。

正如大卫在评论中建议的那样,您可以使用iter = myMap.erase(iter)(或使用没有副作用的范围)来解决问题。