列表擦除迭代器超出范围

时间:2018-06-26 22:26:03

标签: c++ iterator

我有以下代码:

static std::map<int,int> myFunction(std::list<int>& symbols){
    std::map<int,int> currCounts;
    std::map<int,int> payHits;
    for (std::list<int>::iterator l_itr = symbols.begin(); l_itr != symbols.end(); ++l_itr){
        myFunction_helper(*l_itr, l_itr, symbols, currCounts, payHits, 0);
    }
    return payHits;
}

static inline void myFunction_helper(int next, std::list<int>::iterator& pos, std::list<int> remainingSymbols, std::map<int,int> currCounts, std::map<int,int>& payHits, int i){
    currCounts[next] = currCounts.count(next) > 0 ? currCounts[next] + 1 : 1;
    remainingSymbols.erase(pos);
    if (i < numTiles && remainingSymbols.size() > 0){
        if (currCounts[next] == hitsNeeded[next]){
            int pay = symbolPays[next];
            payHits[pay] = payHits.count(pay) > 0 ? payHits[next] + 1 : 1;
        }
        else{
            for (std::list<int>::iterator l_itr = remainingSymbols.begin(); l_itr != remainingSymbols.end(); ++l_itr){
               myFunction_helper(*l_itr, l_itr, remainingSymbols, currCounts, payHits, i+1);
            }
        }
    }
    else{
        payHits[0] = payHits.count(0) > 0 ? payHits[0] + 1 : 1;
    }

}

应该采用一组值并给出一些要求(numTiles(int),hitsNeeded(符号图和需要选择获胜的次数)。我的代码基于Visual Studio(最新版本)构建,但是当我尝试执行它时,首次调用myFunction_helper时出现错误“列表擦除迭代器超出范围”。如何避免这种情况?我故意按值传递了剩余符号,以便可以修改它而不影响其他递归堆栈框架成员。我该如何解决?为什么会引发异常?


解决方案

从参数中删除迭代器。然后,当您迭代时,请使用以下代码段:

int next = *l_itr;
l_itr = symbols.erase(l_itr);
myFunction_helper(next, remainingSymbols, currCounts, payHits, i+1);
symbols.push_front(next);

类似地,外部函数也是如此。将元素推到最前面不会破坏列表的迭代,并且允许我想要的东西(推到最前面也很便宜)。

2 个答案:

答案 0 :(得分:3)

同意以下评论。这是个废话,因为我们对业务案例了解不足,无法提出好的解决方案。我将其保留在编辑后的版本中,因为我刚刚回复了故意破坏的问题,它确实说明了尝试失败的原因。

为什么会引发异常

std::list<int> remainingSymbols是按值传递的,因此pos不再相关。它引用源list,而不是remainingSymbols中源列表的副本。对于另一个list,甚至一个副本,使用迭代器是致命的。

解决方案

常见的解决方案是通过引用remainingSymbols传递std::list<int> & remainingSymbols,但是由于这会破坏回溯,所以您不能这样做。

相反,您将必须为该位置(可能是索引)传递一个不同的标识符。不幸的是,插入和重新迭代list是一项昂贵的任务,几乎总是胜过list的快速插入和删除操作。

答案 1 :(得分:1)

您不能将一个容器中的迭代器与另一个容器中的迭代器一起使用,而可以使用offset,但是对于std::list来说,这是非常无效的。另外,将std::listint一起使用通常不是一个好主意-您的数据很小,并且很可能与数据本身以及缓存未命中相比,用于维护列表项的内存至少多两倍。您最好使用std::vector<int>并传递偏移量,而不是迭代器。另外,您可以使用vector<>使用移动擦除习惯用法,但是即使删除向量中间的int也是相对便宜的,最有可能比跳过std::list个节点的开销便宜。