Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Successor of iterator is not necessarily a regular function: how is it possible?

In page 91 of the book Elements of Programming, Stepanov and McJones say that the concept of Iterator requires a successor function but that is not necessarily regular because

...i = j does not imply that successor(i) = successor(j)...

(see page online)

I understand the converse successor(i) = successor(j) does not imply i=j (for example in two null terminated list) and that successor function may not be defined for some inputs. But I don't understand how could it be possible that i = j can lead to successor(i) != successor(j).

What case would they be referring to? Perhaps some iterator that does random (as in aleatory) hops? or some iterator that has a hidden state and "jumps" differently than the other iterator after pointing to the same element (and comparing equal in this sense).

They immediately jump to refinements (ForwardIterator) that require a regular successor function so it is not clear to me.


Initially I thought that an input iterator can have this property. However is still difficult to me to see if this constitutes a counterexample: (within a certain implementation of STL).

#include <iostream>
#include <sstream>
#include <iterator>
#include <numeric>
#include <cassert>
using std::cout; using std::endl;
int main(){
    std::istream_iterator<int> it1(std::cin); // wait for one input
    std::istream_iterator<int> it2 = it1;
    assert(it1 == it2);
    cout << "*it1 = " << *it1 << endl;
    cout << "*it2 = " << *it2 << endl;
    cout << "now sucessor" << endl;
    ++it1; // wait for one input
    ++it2; // wait for another input
    assert(it1 == it2); // inputs still compare equal !
    cout << "*it1 = " << *it1 << endl;
    cout << "*it2 = " << *it2 << endl;
    assert(it1 == it2); // also here ! and yet they point to different values...
    assert(*it1 == *it2); // assert fails! 
}

(compiled with GCC 6.1)

like image 251
alfC Avatar asked Dec 06 '16 06:12

alfC


People also ask

How do you avoid iterator invalidation in C++?

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.

Is iterator a member function?

Iterators are generated by STL container member functions, such as begin() and end(). Some containers return iterators that support only the above operations, while others return iterators that can move forward and backward, be compared with <, and so on.

Can you increment an iterator?

You can increment your iterator pointer as well as decrement it.

What is an invalid iterator?

When the container to which an Iterator points changes shape internally, i.e. when elements are moved from one position to another, and the initial iterator still points to the old invalid location, then it is called Iterator invalidation. One should be careful while using iterators in C++.


2 Answers

Consider the type iter defined as:

struct iter { unsigned value; };

inline bool operator==(iter const& x, iter const& y) {
  return x.value == y.value;
}
inline bool operator!=(iter const& x, iter const& y) {
  return !(x == y);
}
auto source(iter const& x) {
  return x.value;
}
iter successor(iter const&) {
  std::random_device engine{};
  std::uniform_int_distribution<unsigned> dist{};
  return {dist(engine)};
}

IIRC, iter satisfies the requirements for EoP's Iterator concept: it is Regular, source is a regular function, successor notably is not regular. Given two objects i and j of type iter such that i == j, it is extremely likely that successor(i) != successor(j).

like image 117
Casey Avatar answered Nov 14 '22 21:11

Casey


An example could be a successor function that consumes a stream of data (as they mention in the book).
When you have read the i-th element, you can theoretically invoke the successor function for it only once. If you try to invoke it twice, the results are different.
Simply imagine that successor(i) reads the next element from the stream, that is the i-th+1 element. It actually means to consume it and it won't be available anymore. If you call successor(i) another time, you will get the i-th+2 element from the stream.
Thus, if the inputs are the same (i = j), you have no guarantees that the outputs are the same (successor(i) = successor(j)).

like image 22
skypjack Avatar answered Nov 14 '22 23:11

skypjack