删除元素后 std::map::迭代器的有效性

Validity of std::map::iterator after erasing elements

本文关键字:迭代器 有效性 map 删除 std 元素      更新时间:2023-10-16

我写了一个代码来解决以下问题: 我们有一个map<double,double>,里面有(相对)大量的项目。我们希望合并相邻的项目,以减小地图的大小,尽可能降低一定的"损失因子"。

为此,我首先填充一个包含相邻迭代器和关联的损失因子的列表(假设每个列表元素都有以下类型:

struct myPair {
    map<double,double>::iterator curr, next;
    double loss;
    myPair(map<double,double>::iterator c, map<double,double>::iterator n, 
        double l):  curr(c), next(n), loss(l) {}
};

)。 这是按如下方式完成的:

for (map<double,double>::iterator it1 = myMap.begin(); it1 != --(myMap.end()); 
    it1++) {
    map<double,double>::iterator it2 = it1; it2++;
    double l = computeLoss(it1,it2); 
    List.push(myPair(it1,it2,l));
}

然后,我找到对应于最低损失因子的列表元素,从maperase相应的元素,并在mapinsert一个新元素(合并currnext的结果)。由于这也更改了next之后或之前与元素对应的列表元素curr因此我更新了相应的条目以及相关的损失因子。

(我没有详细介绍如何有效地实现上述内容,但基本上我正在结合双链表和堆)。

虽然erase操作不应使程序的某些特定输入实例的其余迭代器无效,但我在尝试从map中删除元素时正好得到double free or corruption错误。

我试图跟踪这一点,当两个地图元素的firstsecond条目都非常接近时(更准确地说,当currnextfirst非常接近时),似乎会发生这种情况。

奇怪的是,我在填充列表时放置了一个assert,以确保在所有条目中currnext是不同的,并且在删除元素的循环中assert相同的。第二个失败了!

如果有人可以帮助我,我将不胜感激。

附言我很抱歉不是很精确,但我想尽可能降低细节。

更新:这是我从地图中删除元素的方式(非常简化的版本):

while (myMap.size() > MAX_SIZE) {
    t = list.getMin();
    /* compute the merged version ... let's call the result as (a,b) */
    myMap.erase(t.curr);
    myMap.erase(t.next);
    myMap.insert(pair<double,double>(a,b));
    /* update the adjacent entries */
}

myPair中存储的迭代器在容器修改后保持无效。你应该避免这种技术。当您查看头文件时,可能会找到一些适合您任务的草稿?

正如其他人已经提到的,事实证明,使用 double 作为map的密钥是有问题的。特别是在计算值时。

因此,我的解决方案是使用 std::multimap 而不是 map(然后在填充地图后使用相同的键合并元素)。例如,即使a非常接近t.curr键和t.next键或任何其他元素,也可以肯定的是,insert操作会创建一个新元素,以便list中没有现有iterator指向该元素。