In this section, we will use different variants of std::copy:
- Let's first include all headers we need for the data structures we use. Additionally, we declare that we use the std namespace:
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <tuple>
#include <iterator>
#include <algorithm>
using namespace std;
- We will use pairs of integer and string values in the following. In order to nicely print them, we should first overload the << stream operator for them:
namespace std {
ostream& operator<<(ostream &os, const pair<int, string> &p)
{
return os << "(" << p.first << ", " << p.second << ")";
}
}- In the main function, we fill a vector of integer-string pairs with some default values. And we declare a map variable, which associates integer values with string values:
int main()
{
vector<pair<int, string>> v {
{1, "one"}, {2, "two"}, {3, "three"},
{4, "four"}, {5, "five"}};
map<int, string> m;
- Now, we use std::copy_n to copy exactly three integer-string pairs from the front of the vector to the map. Because vectors and maps are completely different data structures, we need to transform the items from the vector using the insert_iterator adapter. The std::inserter function produces such an adapter for us. Please be always aware that using algorithms like std::copy_n combined with insert iterators is the most generic way to copy/insert items to other data structures, but not the fastest. Using the data structure-specific member functions for inserting items is usually the most efficient way:
copy_n(begin(v), 3, inserter(m, begin(m)));
- Let's print what's in the map afterward. Throughout the book, we have often been printing a container's content using the std::copy function. The std::ostream_iterator helps a lot in that regard because it allows us to treat the user shell's standard output as another container we can copy data into:
auto shell_it (ostream_iterator<pair<int, string>>{cout,
", "});
copy(begin(m), end(m), shell_it);
cout << 'n';
- Let's clear the map again for the next experiment. This time, we move items from the vector to the map, and this time, it's all the items:
m.clear();
move(begin(v), end(v), inserter(m, begin(m)));
- We print the new content of the map again. Moreover, as std::move is an algorithm that also alters the data source, we will print the source vector too. This way, we can see what happened to it when it acted as a move source:
copy(begin(m), end(m), shell_it);
cout << 'n';
copy(begin(v), end(v), shell_it);
cout << 'n';
}
- Let's compile and run the program and see what it says. The first two lines are simple. They reflect what the map contained after applying the copy_n and move algorithms. The third line is interesting because it shows that the strings in the vector that we used as move source are now empty. This is because the content of the strings has not been copied but efficiently moved (which means that the map uses the string data in heap memory that was previously referenced by the string objects in the vector). We should usually not access items that were a move source before we reassigned them, but let's ignore that for the sake of this experiment:
$ ./copying_items
(1, one), (2, two), (3, three),
(1, one), (2, two), (3, three), (4, four), (5, five),
(1, ), (2, ), (3, ), (4, ), (5, ),