Фильтруйте и изменяйте элементы в boost :: multi_index из запроса equal_range - PullRequest
1 голос
/ 24 апреля 2019

У меня есть 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 ]

1 голос
/ 24 апреля 2019

Некоторые комментарии к вашему предложенному решению:

  • BMIter не является особенным, как вы, вероятно, подразумеваете, а просто итератором, связанным с первым индексом контейнера.Обратите внимание, что это будет то же самое, что и myIter, когда myView окажется первым индексом.
  • Тем не менее, итераторы хешированных индексов не аннулируются вставками или модификациями , поэтому выВы в безопасности.На самом деле, вы могли бы определить 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;
}
0 голосов
/ 24 апреля 2019

Я думаю, что нашел ответ сам.Вместо того, чтобы просто изменять элементы внутри цикла 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 делает недействительным последнее, но первое остается в силе даже после изменения порядка.

...