-
Notifications
You must be signed in to change notification settings - Fork 2
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.
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.
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 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:
- Using comparison directly:
it == view.end()
; - Implicit comparison using logical inversion:
!!it
(a.k.a double bang trick —operator !
checks if the iterator points to the end of its parentview
, 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++);
}