Let’s slightly modify the example from The Move Iterator Adapter in C++ post:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <algorithm> #include <list> #include <string> #include <vector> /// @warning BUG! PLEASE, DON'T USE! auto deep_copy_to_list(const std::vector<std::string>& src) { std::list<std::string> dst; // constructs the empty list copy(cbegin(src), cend(src), begin(dst)); // cause memory corruption (if !src.empty())! return dst; } |
In the example above the object of type std::list<std::string>
is empty just after the construction and attempt to use std::copy
for copying elements from src
will cause memory corruption is case when the given src
is not empty. However, the standard library provides special adapters which are template classes: std::back_insert_iterator
, std::front_insert_iterator
and std::insert_iterator
. These adapters appends, prepends and inserts the elements to a corresponding container by using push_back()
, push_front()
and insert()
methods accordingly, rather than overwriting the existing elements in the destination container. In other words, the destination container grows in size by one element whenever a value is written through an insert iterator adapter. These adapters can be conveniently created by using function templates std::back_inserter()
, std::front_inserter()
and std::inserter()
, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <algorithm> #include <iterator> // includes standard iterator adapters #include <list> #include <string> #include <vector> auto deep_copy_to_list(const std::vector<std::string>& src) { std::list<std::string> dst; copy(cbegin(src), cend(src), back_inserter(dst)); // ok now return dst; } auto cheap_move_to_list(std::vector<std::string>&& src) { std::list<std::string> dst; copy(make_move_iterator(begin(src)), make_move_iterator(end(src)), back_inserter(dst)); // also ok return dst; } |
Finally, please note, insert adapters are output iterators (see Categories of Iterators in C++) and thus it’s not possible to read through them.