C++ STL iterator
One of the keys to understanding how to use C + + Standard Template Library (STL) containers is to understand how iterators work. Containers such as lists and maps don't behave like arrays, so you can't use a for loop to iterate over the elements. Similarly, because these containers cannot be accessed randomly, simple integer indexes cannot be used. You can use iterators to reference container elements.
The reason STL containers and algorithms work well together is that they know nothing about each other - Alex Stepanov
Iterators are pointer like objects that allow programs to sequentially traverse the elements of a container without exposing the underlying representation. Iterators can advance from one element to the next by incrementing and decrementing them. Each container type has a different iterator associated with it. For example, the iterator forlist < int > is declared as:
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code> <span style="color:#e6db74">std</span>::<span style="color:#e6db74">list</span><<span style="color:#f92672">int</span>>::iterator</code></span></span></span></span>
Iterator class
Iterators fall into several categories because different algorithms need to use different iterators. For example, the std::copy() algorithm needs an iterator that can be incremented, while the std::reverse() algorithm needs an iterator that can be decremented. In the C + + language, the standard defines five different categories.
- Input Iterator
- Read only and can only be read once.
- Example: STD:: istream_ iterator(istream& is)
- output iterators
- Write only
- For example: STD:: ostream_ iterator<int> out_ it (std::cout,", ");
- forward iterators
- Collect input + output iterators
- Example: std::forward_list::iterator,std::unordered_map::iterator
- Bidirectional Iterator
- Like a forward iterator, but there is also an operator –
- Example: std::list::iterator
- random access iterators
- Overloaded operator [], pointer operation
- Example: STD:: vector < int >:: iterator.
You can Here Get more information about these
Iterator Traits
The iterator feature allows the algorithm to access information about specific iterators in a unified way to avoid re implementing all iterators for each specific case when it is necessary to traverse containers of different styles. For example, the lookup element std::list is O(n) complexity, while the std::vector random access element is O(1) complexity (given the index position). It is best for the algorithm to know that the container can be traversed using the + = operator (random access) or only the + + operator (forwarding) to choose a better choice to reduce the complexity of the calculated algorithm.
The iterator features are as follows:
- difference_type:
- Represents the type of iterator distance
- Iterator type differences p2 - p1.
- value_type:
- The type of value pointed to by the iterator
- pointer:
- Pointer value pointed to by iterator
- Usually, = value_type*
- reference:
- The reference value pointed to by the iterator
- Usually, = value_ type&
-
iterator category:
- Identifies the iterator concept modeled by iterators.
- One of the following:
<span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#f92672">struct</span> input_iterator_tag {}; <span style="color:#f92672">struct</span> output_iterator_tag {}; <span style="color:#f92672">struct</span> forward_iterator_tag : input_iterator_tag {}; <span style="color:#f92672">struct</span> bidirectional_iterator_tag : forward_iterator_tag {}; <span style="color:#f92672">struct</span> random_access_iterator_tag : bidirectional_iterator_tag {}; </code></span></span>
Definition of iterator_traits look like:
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#75715e">// The basic version works for iterators with the member type</span> <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> Iterator> <span style="color:#f92672">struct</span> iterator_traits { <span style="color:#f92672">typedef</span> <span style="color:#f92672">typename</span> Iterator::value_type value_type; <span style="color:#f92672">typedef</span> <span style="color:#f92672">typename</span> Iterator::difference_type difference_type; <span style="color:#f92672">typedef</span> <span style="color:#f92672">typename</span> Iterator::pointer pointer; <span style="color:#f92672">typedef</span> <span style="color:#f92672">typename</span> Iterator::reference reference; <span style="color:#f92672">typedef</span> <span style="color:#f92672">typename</span> Iterator::iterator_category iterator_category; }; <span style="color:#75715e">// A partial specialization takes care of pointer types</span> <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> T> <span style="color:#f92672">struct</span> iterator_traits<T *> { <span style="color:#f92672">typedef</span> T value_type; <span style="color:#f92672">typedef</span> <span style="color:#f92672">ptrdiff_t</span> difference_type; <span style="color:#f92672">typedef</span> T *pointer; <span style="color:#f92672">typedef</span> T &reference; <span style="color:#f92672">typedef</span> random_access_iterator_tag iterator_category; }; <span style="color:#75715e">// pointers to const type</span> <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> T> <span style="color:#f92672">struct</span> iterator_traits<<span style="color:#f92672">const</span> T *> { <span style="color:#f92672">typedef</span> T value_type; <span style="color:#f92672">typedef</span> <span style="color:#f92672">ptrdiff_t</span> difference_type; <span style="color:#f92672">typedef</span> <span style="color:#f92672">const</span> T *pointer; <span style="color:#f92672">typedef</span> <span style="color:#f92672">const</span> T &reference; <span style="color:#f92672">typedef</span> random_access_iterator_tag iterator_category; };</code></span></span></span></span>
Sometimes, generic algorithms need to know the value type of their iterator parameters, that is, the type pointed to by the iterator. For example, to exchange the values pointed to by two iterators, you need a temporary variable.
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> Iterator> <span style="color:#f92672">void</span> <span style="color:#a6e22e">swap</span> <span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">Iterator</span> <span style="color:#f8f8f2">a</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">Iterator</span> <span style="color:#f8f8f2">b</span><span style="color:#f8f8f2">)</span> { <span style="color:#f92672">typename</span> Iterator::value_type tmp = *a; *a = *b; *b = tmp; }</code></span></span></span></span>
These features are also obtained by using iterator_ The category member provides knowledge about the basic iterator category to improve the efficiency of the algorithm. The algorithm can use this "tag" to select the most efficient implementation that the iterator can handle without affecting the flexibility of dealing with various iterator types.
In the following example, our goal is to have a single advance algorithm that can automatically execute the correct version according to the iterator category.
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> InputIterator, <span style="color:#f92672">class</span> Distance> <span style="color:#f92672">void</span> <span style="color:#a6e22e">advance</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">InputIterator</span> <span style="color:#f8f8f2">&</span><span style="color:#f8f8f2">i</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">Distance</span> <span style="color:#f8f8f2">n</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">input_iterator_tag</span><span style="color:#f8f8f2">)</span> { <span style="color:#f92672">for</span> (; n > <span style="color:#ae81ff">0</span>; --n) ++i; } <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> BidirectionalIterator, <span style="color:#f92672">class</span> Distance> <span style="color:#f92672">void</span> <span style="color:#a6e22e">advance</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">BidirectionalIterator</span> <span style="color:#f8f8f2">&</span><span style="color:#f8f8f2">i</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">Distance</span> <span style="color:#f8f8f2">n</span> <span style="color:#f8f8f2">bidirectional_iterator_tag</span><span style="color:#f8f8f2">)</span> { <span style="color:#f92672">if</span> (n <= <span style="color:#ae81ff">0</span>) <span style="color:#f92672">for</span> (; n > <span style="color:#ae81ff">0</span>; --n) ++i; <span style="color:#f92672">else</span> <span style="color:#f92672">for</span> (; n < <span style="color:#ae81ff">0</span>; ++n) --i; } <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> RandomAccessIterator, <span style="color:#f92672">class</span> Distance> <span style="color:#f92672">void</span> <span style="color:#a6e22e">advance</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">RandomAccessIterator</span> <span style="color:#f8f8f2">&</span><span style="color:#f8f8f2">i</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">Distance</span> <span style="color:#f8f8f2">n</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">random_access_iterator_tag</span><span style="color:#f8f8f2">)</span> { i += n; } <span style="color:#75715e">// Generic advance algorithm using compile-time dispatching based on function overloading</span> <span style="color:#f92672">template</span> <<span style="color:#f92672">class</span> InputIterator, <span style="color:#f92672">class</span> Distance> <span style="color:#f92672">void</span> <span style="color:#a6e22e">advance</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">InputIterator</span> <span style="color:#f8f8f2">i</span><span style="color:#f8f8f2">,</span> <span style="color:#f8f8f2">Distance</span> <span style="color:#f8f8f2">n</span><span style="color:#f8f8f2">)</span> { advance(i, n, <span style="color:#f92672">typename</span> iterator_traits<Iterator>::iterator_category()); }</code></span></span></span></span>
Writing custom iterators
The iterator feature automatically applies to any iterator class that defines the appropriate member type. The custom iterator should support the following pointers:
- How to retrieve the value of this point
- How to increase / decrease iteration points
- How to compare with other iteration points
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#75715e">#include <algorithm></span> <span style="color:#75715e">#include <exception></span> <span style="color:#75715e">#include <iostream></span> <span style="color:#75715e">#include <iterator></span> <span style="color:#75715e">#include <typeinfo></span> <span style="color:#75715e">#include <vector></span> <span style="color:#f92672">template</span> <<span style="color:#f92672">typename</span> ArrType> <span style="color:#f92672">class</span> MyArray { <span style="color:#f92672">private</span>: ArrType *m_data; <span style="color:#f92672">unsigned</span> <span style="color:#f92672">int</span> m_size; <span style="color:#f92672">public</span>: <span style="color:#f92672">class</span> Iterator { <span style="color:#f92672">public</span>: <span style="color:#75715e">// iterator_trait associated types</span> <span style="color:#f92672">typedef</span> Iterator itr_type; <span style="color:#f92672">typedef</span> ArrType value_type; <span style="color:#f92672">typedef</span> ArrType &reference; <span style="color:#f92672">typedef</span> ArrType *pointer; <span style="color:#f92672">typedef</span> <span style="color:#e6db74">std</span>::bidirectional_iterator_tag iterator_category; <span style="color:#f92672">typedef</span> <span style="color:#e6db74">std</span>::<span style="color:#f92672">ptrdiff_t</span> difference_type; Iterator(pointer ptr) : m_itr_ptr(ptr) {} itr_type <span style="color:#f92672">operator</span>++() { itr_type old_itr = *<span style="color:#f92672">this</span>; m_itr_ptr++; <span style="color:#f92672">return</span> old_itr; } itr_type <span style="color:#f92672">operator</span>++(<span style="color:#f92672">int</span> dummy) { m_itr_ptr++; <span style="color:#f92672">return</span> *<span style="color:#f92672">this</span>; } itr_type <span style="color:#f92672">operator</span>--() { itr_type old_itr = *<span style="color:#f92672">this</span>; m_itr_ptr--; <span style="color:#f92672">return</span> old_itr; } itr_type <span style="color:#f92672">operator</span>--(<span style="color:#f92672">int</span> dummy) { m_itr_ptr--; <span style="color:#f92672">return</span> *<span style="color:#f92672">this</span>; } reference <span style="color:#f92672">operator</span>*() <span style="color:#f92672">const</span> { <span style="color:#f92672">return</span> *m_itr_ptr; } pointer <span style="color:#f92672">operator</span>->() <span style="color:#f92672">const</span> { <span style="color:#f92672">return</span> m_itr_ptr; } <span style="color:#f92672">bool</span> <span style="color:#f92672">operator</span>==(<span style="color:#f92672">const</span> itr_type &rhs) { <span style="color:#f92672">return</span> m_itr_ptr == rhs.m_itr_ptr; } <span style="color:#f92672">bool</span> <span style="color:#f92672">operator</span>!=(<span style="color:#f92672">const</span> itr_type &rhs) { <span style="color:#f92672">return</span> m_itr_ptr != rhs.m_itr_ptr; } <span style="color:#f92672">private</span>: pointer m_itr_ptr; }; MyArray(<span style="color:#f92672">unsigned</span> <span style="color:#f92672">int</span> size) : m_size(size) { m_data = <span style="color:#f92672">new</span> ArrType[m_size]; } <span style="color:#f92672">unsigned</span> <span style="color:#f92672">int</span> <span style="color:#a6e22e">size</span><span style="color:#f8f8f2">()</span> <span style="color:#f92672">const</span> { <span style="color:#f92672">return</span> m_size; } ArrType &<span style="color:#f92672">operator</span>[](<span style="color:#f92672">unsigned</span> <span style="color:#f92672">int</span> idx) { <span style="color:#f92672">if</span> (idx >= m_size) <span style="color:#f92672">throw</span> <span style="color:#e6db74">std</span>::runtime_error(<span style="color:#e6db74">"Index out of range"</span>); <span style="color:#f92672">return</span> m_data[idx]; } Iterator <span style="color:#a6e22e">begin</span><span style="color:#f8f8f2">()</span> { <span style="color:#f92672">return</span> Iterator(m_data); } Iterator <span style="color:#a6e22e">end</span><span style="color:#f8f8f2">()</span> { <span style="color:#f92672">return</span> Iterator(m_data + m_size); } }; <span style="color:#f92672">int</span> <span style="color:#a6e22e">main</span><span style="color:#f8f8f2">()</span> { MyArray<<span style="color:#f92672">double</span>> arr(<span style="color:#ae81ff">3</span>); arr[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">2.6</span>; arr[<span style="color:#ae81ff">1</span>] = <span style="color:#ae81ff">5.2</span>; arr[<span style="color:#ae81ff">2</span>] = <span style="color:#ae81ff">8.9</span>; <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << <span style="color:#e6db74">"MyArray Contents: "</span>; <span style="color:#f92672">for</span> (MyArray<<span style="color:#f92672">double</span>>::Iterator it = arr.begin(); it != arr.end(); it++) { <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << *it << <span style="color:#e6db74">" "</span>; } <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << <span style="color:#e6db74">std</span>::<span style="color:#e6db74">endl</span>; <span style="color:#e6db74">std</span>::<span style="color:#e6db74">vector</span><<span style="color:#f92672">double</span>> vec; <span style="color:#e6db74">std</span>::copy(arr.begin(), arr.end(), <span style="color:#e6db74">std</span>::back_inserter(vec)); <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << <span style="color:#e6db74">"Vector Contents after copy: "</span>; <span style="color:#f92672">for</span> (<span style="color:#e6db74">std</span>::<span style="color:#e6db74">vector</span><<span style="color:#f92672">double</span>>::iterator it = vec.begin(); it != vec.end(); it++) { <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << *it << <span style="color:#e6db74">" "</span>; } <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << <span style="color:#e6db74">std</span>::<span style="color:#e6db74">endl</span>; <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << <span style="color:#f92672">typeid</span>(<span style="color:#e6db74">std</span>::iterator_traits< MyArray<<span style="color:#f92672">double</span>>::Iterator>::iterator_category()) .name() << <span style="color:#e6db74">std</span>::<span style="color:#e6db74">endl</span>; <span style="color:#f92672">return</span> <span style="color:#ae81ff">0</span>; } <span style="color:#75715e">/*OUTPUT MyArray Contents: 2.6 5.2 8.9 Vector Contents after copy: 2.6 5.2 8.9 FSt26bidirectional_iterator_tagvE */</span></code></span></span></span></span>
Iterators and loop ranges
Range based for loop (or range for short) and auto are one of the most important features added to C++11 standard.
The syntax template for the range for loop is as follows:
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#f92672">for</span> (range_declaration : range_expression) { <span style="color:#75715e">// loop body </span> }</code></span></span></span></span>
In C++11/C++14, the above format produces code similar to the following:
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code>{ <span style="color:#f92672">auto</span>&& range = range_expression ; <span style="color:#75715e">// beginExpr is range.begin() and endExpr is range.end()</span> <span style="color:#f92672">for</span> (<span style="color:#f92672">auto</span> b = beginExpr, e = endExpr; b != e; ++b) { range_declaration = *b; <span style="color:#75715e">// loop body</span> } } </code></span></span></span></span>
Typical usage of a range based for loop:
<span style="color:#333333"><span style="background-color:#ffffff"><span style="background-color:#201e2f"><span style="color:#f8f8f2"><code><span style="color:#75715e">// Iterate over STL container</span> <span style="color:#e6db74">std</span>::<span style="color:#e6db74">vector</span><<span style="color:#f92672">int</span>> v{<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>, <span style="color:#ae81ff">4</span>}; <span style="color:#f92672">for</span> (<span style="color:#f92672">const</span> <span style="color:#f92672">auto</span> &i : v) <span style="color:#e6db74">std</span>::<span style="color:#e6db74">cout</span> << i << <span style="color:#e6db74">"</span><span style="color:#e6db74">\n</span><span style="color:#e6db74">"</span>;</code></span></span></span></span>
The range for loop works by creating an iterator pointing to the first element of the vector, then accessing each element of the vector in turn until the iterator reaches the last element of the vector, and then the loop terminates. This phenomenon can be observed in cppinsight here.