C ++: увеличить итератор диапазона, указывающий на неправильный элемент

Я столкнулся со странной проблемой. у меня есть vector<pair<bool, int>> из которого мне нужно прочитать (и, возможно, записать) только те элементы вектора, для которых логическое значение пары является истинным. Я использую фильтр увеличения диапазона и обратные адаптеры, чтобы сделать это.

Тем не менее, я заметил, что порядок адаптеров, то есть ли я использую обратный | фильтруется или фильтруется | обратное дает разные результаты. На самом деле, когда я использую фильтрованный | затем, когда я использую итератор для преобразованного диапазона, чтобы изменить логическое значение пары, затем итератор после изменения указывает на другой элемент вектора. Это не происходит, когда я использую обратное | фильтруют. Ниже приведен код, демонстрирующий проблему. Любые идеи относительно того, почему это происходит, очень ценятся!

#include <boost/range/adaptors.hpp>
#include <vector>
#include <utility>
#include <iostream>

using namespace boost::adaptors;

using container_type = std::vector<std::pair<bool,int>>;

struct to_include {
bool operator()(const std::pair<bool,int>& x) {
return x.first;
}
};

int main() {
container_type container;

/* element0: 1, 1 */
/* element1: 1, 2 */
/* element2: 1, 3 */
for(size_t i=0; i!=3; ++i) container.push_back(std::make_pair(true, i+1));
container_type container_cpy = container;

/* filter and then reverse */
auto fr = container | filtered(to_include()) | reversed;
auto fr_it1 = fr.begin();
auto fr_it2 = std::next(fr_it1);
fr_it2->first = false;

std::cout << "FILTER AND THEN REVERSE\n";
std::cout << fr_it2->first << " " << fr_it2->second << '\n'; /* prints (1,1) instead of (0,2) */

/* reverse and then filter */
auto rf = container_cpy | reversed | filtered(to_include());
auto rf_it1 = rf.begin();
auto rf_it2 = std::next(rf_it1);
rf_it2->first = false;

std::cout << "\nREVERSE AND THEN FILTER\n";
std::cout << rf_it2->first << " " << rf_it2->second << '\n'; /* prints (0,2) */

return 0;
}

2

Решение

Это тонкий вопрос. Дело в том, что после изменения элемента, на который указывает fr_it2Вы также неявно модифицируете fr_it1 так как fr это ленивый вид на оригинальном ассортименте. Это означает, что transformed фильтр должен быть пересчитан. Это очень неинтуитивное свойство, потому что для активных диапазонов STL модификации через итераторы не изменяют сами итераторы, но для отложенных диапазонов это уже не так!

На самом деле, если вы печатаете весь fr а также rf диапазоны, использующие «свежие» итераторы, вы увидите, что их содержимое фактически одинаково.

fr_it2->first = false;
for (auto e : fr) std::cout << e.first << e.second << ";"; // prints 13;11
...
rf_it2->first = false;
for (auto e : rf) std::cout << e.first << e.second << ";"; // prints 13;11

Живой Пример 1. Так что на самом деле средний элемент действительно удален!

Я думаю, вам не следует изменять элементы с помощью итераторов в адаптированный диапазон, а с помощью итераторов в ваш основной контейнер, например:

auto fr_it1 = container.begin();
...
auto rf_it1 = container_cpy.begin();

Живой Пример 2.
Если вы сделаете это, вы получите согласованные результаты, которые показывают «0 2» для обоих подходов.

1

Другие решения

Других решений пока нет …