从 equal_range 查询过滤和修改 boost::multi_index 中的元素
Filter and modify elements in boost::multi_index from an equal_range query
我有一个 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 无效。
执行此操作的正确方法是什么?谢谢!
我想我自己找到了答案。不是简单地修改 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);
}
请注意,BMIIter
和 myIter
是不同的迭代器类型 - 前者是元素本身的迭代器,而后者是特定于 myView 的迭代器。修改 multi_index 中的元素会使后者无效,但前者即使在重新排序后仍然有效。
对您提出的解决方案的一些评论:
BMIter
并不像您暗示的那样特别,而只是与容器的第一个索引关联的迭代器。请注意,当 myView
恰好是第一个索引时,这将与 myIter
相同。
- 然而,哈希索引的迭代器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;
}
我有一个 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 无效。
执行此操作的正确方法是什么?谢谢!
我想我自己找到了答案。不是简单地修改 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);
}
请注意,BMIIter
和 myIter
是不同的迭代器类型 - 前者是元素本身的迭代器,而后者是特定于 myView 的迭代器。修改 multi_index 中的元素会使后者无效,但前者即使在重新排序后仍然有效。
对您提出的解决方案的一些评论:
BMIter
并不像您暗示的那样特别,而只是与容器的第一个索引关联的迭代器。请注意,当myView
恰好是第一个索引时,这将与myIter
相同。- 然而,哈希索引的迭代器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;
}