Skip to content

Iterators

Alex Qzminsky edited this page Jun 7, 2020 · 11 revisions

Iterators

Iterators are internal pointing objects tied with views. Every iterator stores two pointers:

  • pointer to the tied view (its "parent view");
  • pointer to the UTF-8 character inside the parent view.

Relation between iterators and views leads to make possible the range control regarding iterators — and switching the iteration direction as well. So, when the view is reverse()-ed, all of its iterators will consider it.

Since iterators are attached to their views, they cannot be returned directly from string's methods — an iterator cannot exist without its own view! In case of returning an iterator, you have to free() it first. An unbound iterator cannot be compared or computed with; it must be tied with some complementary view object by the bind(view) method.

Some arithmetics is unexpected in the indicated context. For example, the subtraction actually increases the subtrahend, so it works:

utf::string Line{ "Котлетка с пюрешкой" };

// Subtraction returns the count of incremental steps
// from subtracted iterator to another one
std::cout << Line.chars().find("пюрешкой").begin() - Line.chars().begin();

The view class has two methods pointing to the ends of the specific range: begin() and end(). The end() iterator is always pointing to the next-to-last character of the view.

pic

Movement

An iterator can be moved in two directions: forward and backward consider its parent view direction option. So,

utf::string Line{ "I am Groot" };

auto view = Line.chars().backward();
// Now, begin() is pointing to the last character of Line

for (auto it = view.begin(); it != view.end(); ++it)
{
    std::cout << char(*it);    // prints "toorG ma I"
}

Due to the fact that we changed direction, incrementing the iterator it actually leads to reducing its internal pointer.

Iterators provides all of incrementing/decrementing operators and its post-versions as well.

As we can shift an iterator to the single step, it is possible to add or subtract the count of several steps using operators + and -. Note that every iterator always stays within the range [begin(); end()] — so, increasing of the end() iterator has no effect, i.e., it stays the same.

Comparison

Iterators are comparable between each other. Two iterators are equal if they stores the same pointing address.

utf::string Line{ "La tour Eiffel" };

assert(Line.chars().begin() + 5 == Line.chars().end() - 9);  // ok, it's 'u'

Next important comparison operation is the predicate "less". By design, the iterator x is less than y, if it is possible to make them equal each other by increasing x sequentally (hypothetically). Note that the iterator begin() must be always less than end(), so, view's direction is also taken into account in this case.

Other comparison operators are defined via combination of less and equality with inversion:

Operation Defined as
it_1 != it_2 !(it_1 == it_2)
it_1 <= it_2 (it_1 == it_2) ¦¦ (it_1 < it_2)
it_1 > it_2 it_2 < it_1
it_1 >= it_2 (it_1 == it_2) ¦¦ (it_2 < it_1)

Dereferencing

Dereferencing operation is different from STL iterators'. Because the structure of the string's buffer is irregular, returning of the reference to the character is pointless. So, operator * and operator [] returns code points instead.

These two writings are equal: *(Line.chars().begin() + 5) and Line.chars().begin()[5].

Note that dereferencing of the end() iterator throws an out_of_range exception. So, you must check the iterator before; there are two ways to do this:

  1. Using comparison directly: it == view.end();
  2. Implicit comparison using logical inversion: !!it (a.k.a double bang trickoperator ! checks if the iterator points to the end of its parent view, after which the result is inverted). For example:
utf::string Line{ "I am Groot" };

auto view = Line.chars().backward();

for (auto it = view.begin(); !!it;)  // ⇐ "!!it" is equal to "it != view.end()"
{
    std::cout << char(*it++);
}
Clone this wiki locally