当我添加新元素时,Unordered_map表现得很奇怪

时间:2019-05-01 10:43:07

标签: c++ c++11 stl

此行无法正常工作:

for (auto prod : productions_[*productionNonterm])
                productions_[nonterminal].push_back(prod);

如果productions _ [* productionNonterm]只有1个元素,那么一切都很好。但是,如果它至少包含2个元素,则会修改productionNonterm,我也不知道为什么。

vector<string> nonterminals_;
unordered_map<string, vector<string>> productions_;

for (const auto &nonterminal : nonterminals_) {
    for (auto productionNonterm = productions_[nonterminal].begin(); productionNonterm != productions_[nonterminal].end(); ++productionNonterm) {
        if (cntNonterminalsInProduction(*productionNonterm) == 1 && cntTerminalsInProduction(*productionNonterm) == 0) {
            nonterminals_.erase(find(nonterminals_.begin(), nonterminals_.end(), *productionNonterm));

            for (auto prod : productions_[*productionNonterm])
                productions_[nonterminal].push_back(prod);

            productions_[*productionNonterm].erase(productions_[*productionNonterm].begin(), productions_[*productionNonterm].end());

            productions_[nonterminal].erase(productionNonterm);
            --productionNonterm;

        }
    }
}

2 个答案:

答案 0 :(得分:1)

迭代器productionNonterm的问题,它在循环过程中变得无效:

一旦开始循环

    for (auto prod : productions_[*productionNonterm])
        productions_[nonterminal].push_back(prod);

您将使用指向productionNonterm中一个(第一个)元素的有效迭代器(productions_[nonterminal])。

但是在第一次执行循环的主体之后-向量productions_[nonterminal]将重新分配其元素(由于增长),并且指针(迭代器)将无效...

答案 1 :(得分:0)

在迭代时修改集合很棘手,通常不值得在容器中支持它的开销。在这种情况下(向量),一旦向量在增长时重新分配,就使迭代器(即指向向量存储的指针)失效。

看看vector::push_back,尤其是关于迭代器有效性的讨论。

在实践中,可以通过调整向量的大小来避免出现此特定问题,但是在跟踪相对于迭代器放置新元素的位置等方面也存在一些问题。通常,即使在以下情况下,它也会导致难以遵循的代码这是正确的(或更糟的是,看上去却有细微的问题)。

我建议您重写为两遍方法,收集一次添加或删除的元素,然后删除它们,等等。除非您有非常好的(并且经过测量!)性能理由不这样做,否则以便于理解和维护。回想一下vector :: erase可以取一个范围。

这也值得问:这里的顺序重要吗?如果您使用的是std :: unordered_set,在这种情况下您就不会遇到这个特殊的问题。

在“修复”此方法之前,先了解一下您对该方法的看法。