In this section, we use some iterator wrappers just for the sake of showing that they exist and how they can help us in everyday programming tasks.
- We need to include some headers first:
#include <iostream>
#include <string>
#include <iterator>
#include <sstream>
#include <deque>
- Declaring that we use namespace std spares us some typing later:
using namespace std;
- We start with std::istream_iterator. We specialize it on int. This way, it will try to parse the standard input to integers. For example, if we iterate over it, it will look as if it was std::vector<int>. The end iterator is instantiated of the same type but without any constructor arguments:
int main()
{
istream_iterator<int> it_cin {cin};
istream_iterator<int> end_cin;
- Next, we instantiate std::deque<int> and just copy over all the integers from the standard input into the deque. The deque itself is not an iterator, so we wrap it into std::back_insert_iterator using the std::back_inserter helper function. This special iterator wrapper will execute v.push_back(item) with each of the items we get from the standard input. This way the deque is grown automatically!
deque<int> v;
copy(it_cin, end_cin, back_inserter(v));
- In the next exercise, we use std::istringstream to copy items into the middle of the deque. So, let's first define some example numbers in the form of a string and instantiate the stream object from it:
istringstream sstr {"123 456 789"};
- Then, we need a hint of where to insert into the deque. It will be the middle, so we use the begin pointer of the deque and feed it to the std::next function. The second argument of this function says that it will return an iterator advanced by v.size() / 2 steps, that is, half the deque. (We cast v.size() to int because the second parameter of std::next is difference_type of the iterator used as the first parameter. In this case, this is a signed integer type. Depending on the compiler flags, the compiler might warn at this point if we didn't cast explicitly.)
auto deque_middle (next(begin(v),
static_cast<int>(v.size()) / 2));
- Now, we can copy parsed integers step by step from the input string stream into the deque. Again, the end iterator of a stream iterator wrapper is just an empty std::istream_iterator<int> without constructor arguments (that is, the empty {} braces in the code line). The deque is wrapped into an inserter wrapper, which is an std::insert_iterator, which is pointed to the deque's middle using the deque_middle iterator:
copy(istream_iterator<int>{sstr}, {}, inserter(v, deque_middle));
- Now, let's use std::front_insert_iterator to insert some items at the front of the deque:
initializer_list<int> il2 {-1, -2, -3};
copy(begin(il2), end(il2), front_inserter(v));
- In the last step, we print the whole content of the deque out to the user shell. The std::ostream_iterator works like an output iterator which, in our case, just forwards all the integers it gets copied from to std::cout and then appends ", " after each item:
copy(begin(v), end(v), ostream_iterator<int>{cout, ", "});
cout << 'n';
}
- Compiling and running the program yields the following output. Can you identify which number was inserted by which code line?
$ echo "1 2 3 4 5" | ./main
-3, -2, -1, 1, 2, 123, 456, 789, 3, 4, 5,