从equal_range查询中筛选和修改 boost::multi_index 中的元素

Filter and modify elements in boost::multi_index from an equal_range query

本文关键字:multi 元素 boost index range equal 查询 筛选 修改      更新时间:2023-10-16

我有一个hashed_non_unique视图的boost::multi_index。我想完成的是,在该视图中给定一个键,使用

pair<myIter, myIter> iterRange = myView.equal_range(key);
for (myIter iter = iterRange.first; iter != iterRange.second; ++iter) {
// ...
}

以查找与该键关联的所有元素。然后,通过筛选器运行这些元素

bool filter(Element e) { /* some filtering logic*/ }

并使用修饰符修改过滤结果

void modifier(Element e) { /* modify the elements with e.g. myView.modify() */ }

但是,简单地将这些部分放在一起是行不通的,因为修改元素会导致multi_index重新排序,从而使我的 iterRange 无效。

正确的方法是什么?谢谢!

对您提出的解决方案的一些评论:

  • BMIter并不像您似乎暗示的那样特殊,而只是与容器的第一个索引关联的迭代器。请注意,当myView恰好是第一个索引时,这将与myIter相同。
  • 尽管如此,哈希索引的迭代器不会因插入或修改而失效,因此您是安全的。事实上,您可以将iters定义为vector<myIter>并直接存储迭代器,而无需任何进一步的转换 - 您仍然可以达到修改后不受潜在重新排序影响的预期效果。
  • 即使您正在做的事情完全没问题,但如果您要挤压一些额外的性能,请注意,当键保持等效时,对散列索引中元素的修改不会更改基础顺序,因此在遍历等效键范围时,重新排序可能会影响您的唯一方式是当修改后的元素直接到范围之后(即, 就在iterRange.second之前)。有了这个想法,您可以省去iters技巧,如下所示:

 

for (myIter iter = iterRange.first; iter != iterRange.second; ) {
auto nextIter = std::next(iter);
if (filter(*iter)) {
myView.modify(iter, modifier);
if (nextIter != iterRange.second && std::next(iter) == iterRange.second)
iterRange.second = iter;
}
iter = nextIter;
}

我想我自己已经找到了答案。我们不是简单地修改 for 循环中的元素,而是需要先缓存元素,然后再修改它们以避免改变顺序。这里的诀窍是,不是将迭代器缓存到这个特定的视图,而是将迭代器缓存到元素本身,即

vector<BMIIter> iters;
for (myIter iter = iterRange.first; iter != iterRange.second; ++iter) {
if (filter(*iter)) {
iters.push_back(myBMI.iterator_to(*iter));
}
}
for (auto iter : iters) {
myBMI.modify(iter, modifier);
}

请注意,BMIItermyIter是不同的迭代器类型 - 前者是元素本身的迭代器,而后者是特定于 myView 的迭代器。修改multi_index中的元素会使后者无效,但前者即使在重新排序后仍然有效。