I have been using advance
on some iterators
, but I am afraid of a possible leapfrog above end()
. I would like to make sure my iterators stay between the bounds, I thought of the distance
but it seems it does not return what I would be expecting (non-positive values when iterators overpass end()
). How would you make sure there is no leapfrog?
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
int main () {
list<int> mylist;
for (int i=0; i<10; i++) mylist.push_back (i*10);
list<int>::const_iterator first = mylist.begin();
const list<int>::const_iterator last = mylist.end();
cout << "The distance is: " << distance(first,last) << endl; // 10
advance(first, 10);
cout << "The distance is: " << distance(first,last) << endl; // 0
advance(first, 1);
cout << "The distance is: " << distance(first,last) << endl; // 10
advance(first, 10);
cout << "The distance is: " << distance(first,last) << endl; // 0
return 0;
}
Here is the output:
The distance is: 10
The distance is: 0
The distance is: 10
The distance is: 0
To avoid invalidation of references to elements you can use a std::deque if you do not insert or erase in the middle. To avoid invalidation of iterators you can use a std::list.
Dereferencing: An input iterator can be dereferenced, using the operator * and -> as an rvalue to obtain the value stored at the position being pointed to by the iterator. 4. Incrementable: An input iterator can be incremented, so that it refers to the next element in the sequence, using operator ++().
In something like an std::vector the ::end() iterator will point to one past the last element. You can't dereference this iterator but you can compare it to another iterator. If you compare another iterator to end() you know you've reached the end of the container.
For reference, std::vector::swap does not invalidate iterators.
advance() past end() results in undefined behaviour. You are going to have to test as you go per this snippet:
template <class Iter, class Incr>
void safe_advance(Iter& curr, const Iter& end, Incr n)
{
size_t remaining(std::distance(curr, end));
if (remaining < n)
{
n = remaining;
}
std::advance(curr, n);
}
You need to think about what happens when you don't move the full amount (curr
returned as end()
.
It is inefficient to call distance
or advance
on anything other than models of RandomAccessIterator
: the calls are O(n) where n represents the distance.
Also, the list
is not truly a model of Sequence
in that its size
method is not guaranteed to be constant (or even amortized constant), indeed it could perfectly well be O(n).
Looking at the code (and if you cannot use anything else than a list
) there are therefore two possibilities:
end
at each step.Let's act on this:
// Advance one at a time
// replace calls to advance by this function
typedef list<int>::const_iterator const_iterator;
const_iterator safe_advance(const_iterator it, const_iterator end, size_t n)
{
for (size_t i = 0; i != n && it != end; ++i) { ++it; }
return it;
}
// Precompute size
size_t const size = list.size();
size_t remaining = size;
const_iterator begin = list.begin();
size_t ad = std::min(10, remaining);
advance(begin, ad);
remaining -= ad;
ad = std::min(1, remaining);
advance(begin, ad);
remaining -= ad;
It is tedious to keep this count going though...
EDIT:
Addressing David's rightful concerns to generalize the solutions:
// Advance `it` from n, or up to end
// returns the number of steps that could not be performed
template <typename Iterator>
size_t safe_advance(Iterator& it, Iterator const& end, size_t n)
{
size_t i = 0;
for (; i != n && it != end; ++i, ++it);
return n - i;
}
Note that for Bidirectional Iterators, advance
is usable with negative distances, however this would require bringing in begin
as well and would become really tedious. As such, I much prefer a second method:
template <typename BI>
size_t safe_reverse(BI& it, BI const& begin, size_t n)
{
for (; n != 0 && it != begin; --n, --it);
return n;
}
And finally, though I won't do it here, it would be good to specialize the templates for RandomAccessIterator
since those operations can be done in O(1) with such ones.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With