Boost logo

Boost Users :

Subject: Re: [Boost-users] Boost range iterator pointing to wrong element
From: Nathan Ridge (zeratul976_at_[hidden])
Date: 2013-10-09 02:52:24


>> struct to_include { >> bool operator()(const std::pair<bool,int>& x) { >> return x.first; >> } >> }; > [...] >> /* 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; > > This is just a guess, but it strikes me as potentially dodgy to retain > iterators to a filtered sequence after modifying the result of the > filter predicate on that sequence. (This seems like something that > would invalidate iterators, just like certain collection modifications do.) Gavin is right. Filtered ranges assume that the value of the predicate on sequence elements does not change during the iterator's lifetime. You are breaking that assumption, and so anything can happen. (I can't find any documentation about this assumption, however. We should probably add some.) If you're wondering how the result that you're seeing comes about, it's like this:   - reverse_iterator stores an iterator of the underlying     range not to the element it is pointing to, but to the     next position. This is because there is a past-the-end     iterator but not a before-the-begin one, so we need     the begin() of the reversed range to store the end()     of the underlying range, which, being the past-the-end     iterator, is one position ahead of what the begin() of     the reversed range is actually pointing to, which is     the last actual element)       - so reverse_iterator's dereference operator calls         prior() on the stored underlying iterator to get the         underlying iterator we actually want to dereference.   - filter_iterator simply stores an iterator of the     underlying type to the element it is pointing to       - so filter_iterator's dereference operator just         dereferences the underlying iterator.   - When the outer range is the reversed range, you     dereference the reverse_iterator, which calls prior()     on the filter_iterator.       - prior() on a filter_iterator walks the range backwards         until an element matching the predicate is found.         since you've modified the (0, 2) element so it no         longer matches the predicate, it skips it and goes         on to the (1, 1) element, which is what you rr   - When the outer range is the filtered range, you     dereference the filter_iterator which just derefernces     the underlying reverse_iterator. No calls to the     predicate are made, and (0, 2) element is returned. So to sum it up, the difference is referring to the current element of the filtered range in one case, and referring to the next element and calling prior() to get at the current element in the other case. The two are equivalent under the assumption that the predicate's value does not change while you're iterating. Regards, Nate


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net