Iterators
-
An iterator is simply an object that can iterate over elements. An iterator represents a certain position in a container and allows to navigate from element to element. An iterator is a pure abstraction, so anything that behaves like an iterator is an iterator!
-
An iterator must support these fundamental operations that define its behavior:
-
Operator
*“dereferences” the iterator and returns the element at the current position. -
Operator
++moves the iterator to the next element. Most iterators also define the operator--that moves the iterator to the previous element. -
Operators
==and!=return whether two iterators represent the same position within a container. -
Operator
=assigns an iterator and sets the position of the element to which it refers.
As you can see, iterators’ behavior is very much like that of pointers. But iterators let you work with more complicated data structures. Their real behavior and implementation depend on the data structure over which they iterate.
-
-
All container classes provide the same basic methods (member functions) that allow you to use iterators to navigate over their elements. The most important ones are
beginandend.-
beginreturns an iterator that refers to the element at the beginning of the container. -
endreturns an iterator that represents the end of the container. This position is after the last element in the container.
As you can see,
beginandenddefine a half-open range that includes the first element but excludes the last. -
-
An implication of the behavior of
beginandendis that you can write for loops that look like this.vector<int> numbers = {1, 2, 3, 4}; for (vector<int>::iterator itr = numbers.begin(); itr != numbers.end(); ++itr) { cout << *itr << " "; } cout << endl;The above code prints
1 2 3 4
With C++11, you can replace
vector<int>::iteratorwithautoand the type ofitrwill be automatically determined.vector<int> numbers = {1, 2, 3, 4}; for (auto itr = numbers.begin(); itr != numbers.end(); ++itr) { cout << *itr << " "; } cout << endl;C++11 also introduced range-based for loops, so you can simplify your code even further as you iterate through elements in a container.
vector<int> numbers = {1, 2, 3, 4}; for (auto number : numbers) { cout << number << " "; } cout << endl; -
Every STL container defines two iterator types.
-
container::iteratorprovides read and write access to the container’s elements. -
container::const_iteratorprovides read-only access to the container’s elements.
-
Iterator Categories
-
Iterators are divided into several categories based on their abilities. Each container class in the STL provides iterators that belong to one of the following three categories.
-
Forward iterators only have the basic abilities outlined in the beginning of this section. Iterators of the
forward_listcontainer class are forward iterators. The iterators of the container classesunordered_set,unordered_multiset,unordered_mapandunordered_multimapare “at least” forward iterators. -
Bidirectional iterators are able to iterate both forward and backward. Iterators of the container classes
list,set,multiset,mapandmultimapare bidirectional iterators. -
Random-access iterators have all the abilities of bidirectional iterators in addition to being able to perform random access. You can add and subtract offsets, compute distance between two random-access iterators and compare them using relational operators, such as
<and>. Iterators ofvector,deque,arrayandstringare random-access iterators.
In addition to the above categories, there are two other categories.
-
Input iterators provide read-only access while iterating forward. Input stream iterators are an example thereof.
-
Output iterators provide write-only access while iterating forward. Output stream iterators are an example thereof.
-
Iterator Functions
-
The C++ library provides some auxiliary functions for dealing with iterators.
-
advance()increments the position of an iterator passed as the argument. -
next()andprev()return an iterator at the position before or after the iterator passed as the argument. -
distance()processes the difference between two iterators. -
iter_swap()swaps the values to which two iterators refer.
Iterator Adapters
-
Remember that anything that behaves as an iterator is an iterator. Conveniently, STL provides some special iterators that allow algorithms to operate in reverse, in insert mode and with streams.
-
Here is an example that inserts the elements of one vector in the middle of another:
vector<int> numbers = {183, 203, 281, 370, 376}; vector<int> otherNumbers = {203, 280}; vector<int>::iterator it = find(numbers.begin(), numbers.end(), 281); insert_iterator<vector<int>> insertIt(numbers, it); copy(otherNumbers.begin(), otherNumbers.end(), insertIt);Whereas normally the
std::copyfunction overwrites existing elements in the container, using an insert iterator allows us to instead insert new elements without erasing the old ones. So after callingstd::copy, thenumbersvector contains the elements{183, 203, 203, 280, 281, 370, 376}.
Reverse Iterators
-
Reverse iterators allow us to process elements in a container in reverse order.
-
Reverse iterators redefine increment and decrement operators so that they behave in reverse. Thus, if you use these iterators instead of ordinary iterators, algorithms process elements in reverse order.
-
All containers that provide bidirectional iterators also provide reverse iterators.
-
Here is an example of code that uses a reverse iterator to print the elements of a vector in reverse order:
vector<int> numbers = {183, 203, 280, 281, 370, 376}; reverse_iterator<vector<int>::iterator> reverseIt; for (reverseIt = numbers.rbegin(); reverseIt != numbers.rend(); ++reverseIt) { cout << *reverseIt << " "; }The output is
376 370 281 280 203 183.
Insert Iterators
-
Insert iterators, also called inserters, are iterator adapters that transform an assignment of a new value into an insertion of that new value. By using insert iterators, algorithms can insert values when they would normally overwrite old ones.
-
All insert iterators are considered output iterators, so they provide only the ability to assign new values.
Stream Iterators
-
A stream iterator allows you to use a stream as a source or destination of algorithms. In particular, an input stream iterator can be used to read elements from an input stream (such as
cinor an input file stream), and an ostream iterator can be used to write values to an output stream (such ascoutor an output file stream). -
Below is some of the interface provided by istream iterators:
-
istream_iterator<T>(istream)creates an istream iterator for an input stream (istream) and reads the first value. -
istream_iterator<T>()creates an end-of-stream iterator. -
*iterreturns the value that was read before. -
iter = valuewrites value toostream, followed bydelimif set. This is equivalent toostream << value << delim. -
++iterreads the next value from the stream and returns its position. -
iter++reads the next value but returns an iterator for the previous value.
-
-
Below is some of the interface provided by ostream iterators:
-
ostream_iterator<T>(ostream)creates an ostream iterator for an output stream (ostream). -
ostream_iterator<T>(ostream, delim)creates an ostream iterator for an output streamostream, withconst char* delimas the delimiter between the values. -
iter = valuewrites value toostream, followed bydelimif set. This is equivalent toostream << value << delim.
-
-
Consider this snippet of code for example:
vector<int> inputNumbers; copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(inputNumbers));It would read all integers from standard input and insert them in the
inputNumbersvector. -
Here’s another example and combines input stream iterators and output stream iterators:
copy(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, "\n"));This example reads all integers from standard input and prints them to standard output separated by a newline character.