从equal_range查询中过滤和修改boost :: multi_index中的元素

时间:2019-04-23 22:57:07

标签: c++ boost boost-multi-index

我有一个boost::multi_index和一个hashed_non_unique视图。我想完成的是,鉴于该视图中的关键,请使用

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无效。

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

2 个答案:

答案 0 :(得分:1)

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

    如您所暗示的那样,
  • BMIter并不特殊,而仅仅是与容器的第一个索引关联的迭代器。请注意,当myIter恰好是该第一个索引时,它将与myView相同。
  • 尽管如此,哈希索引are not invalidated by insertions or modifications的迭代器还是很安全的。实际上,您可以将iters定义为vector<myIter>并直接存储迭代器,而无需任何进一步的转换-您仍将达到预期的效果,即不受修改后潜在重新排序的影响。
  • 即使您所做的一切都很好,但是如果您要压缩一些其他性能,请注意,对哈希索引does not change the underlying ordering when the key remains equivalent中的元素进行修改,因此遍历的唯一方式可能会在遍历时影响您等效键的范围是指修改后的元素直接在该范围的 之后(即,在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;
}

答案 1 :(得分:0)

我想我自己找到了答案。我们需要先缓存元素,然后再修改它们,以免改变顺序,而不是简单地修改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中的元素会使后者无效,但是即使重新排序后,前者仍然保持有效。