当我添加一个新元素时,Unordered_map 的行为很奇怪
Posted
技术标签:
【中文标题】当我添加一个新元素时,Unordered_map 的行为很奇怪【英文标题】:Unordered_map acts strange when I add a new element 【发布时间】:2019-05-01 10:43:06 【问题描述】:这行不正常:
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;
【问题讨论】:
修改std::vector
会使该向量中的所有迭代器无效。因此,您不能在通过基于范围的 for 循环(它只是映射到从 .begin()
到 .end()
的普通 for 循环)对其进行迭代时修改 nonterminals_
。 productions_[nonterminal]
也是如此,它也是一个向量。你到底想在这里实现什么?这看起来应该以更易读的形式完全重写......
具体应该根据什么逻辑删除和添加元素?
【参考方案1】:
迭代器 productionNonterm
的问题,它在循环期间变得无效:
一旦你开始循环
for (auto prod : productions_[*productionNonterm])
productions_[nonterminal].push_back(prod);
您将使用一个有效的迭代器 (productionNonterm
) 指向 productions_[nonterminal]
中的一个(第一个)元素。
但在第一次执行循环体后 - 向量 productions_[nonterminal]
将重新分配其元素(由于增长)并且您的指针(迭代器)将失效...
【讨论】:
【参考方案2】:在迭代集合的同时修改集合很棘手,而且通常不值得在容器中支持它的开销。在这种情况下(向量),一旦向量在增长时重新分配,您就会使迭代器(即指向向量存储的指针)无效。
看看vector::push_back,特别是关于迭代器有效性的讨论。
在实践中,您可以通过调整向量大小来避免这个特殊问题,但也存在跟踪相对于迭代器放置新元素的位置等问题。通常,它会导致难以遵循的代码,即使当它是正确的(或者更糟的是,看起来但有一些微妙的问题)。
我建议您重写为两遍方法,在一遍中收集要添加或删除的元素,然后删除它们等。除非您有非常好的(并且经过衡量!)性能理由不这样做,否则它会继续更容易理解和维护。回想一下,vector::erase 可以有一个范围。
还有一个问题:这里的顺序重要吗?如果您使用 std::unordered_set,在这种情况下您不会遇到这个特殊问题。
在“修复”这个方法之前,看看你对这种方法的看法。
【讨论】:
以上是关于当我添加一个新元素时,Unordered_map 的行为很奇怪的主要内容,如果未能解决你的问题,请参考以下文章
Qt - std::unordered_map - 销毁时间