As std::copy is one of the simplest STL algorithms, its implementation is very short. Let's have a look at how it could be implemented:
template <typename InputIterator, typename OutputIterator>
OutputIterator copy(InputIterator it, InputIterator end_it,
OutputIterator out_it)
{
for (; it != end_it; ++it, ++out_it) {
*out_it = *it;
}
return out_it;
}
This looks exactly as one would implement the copying of items from one iterable range to the other by hand, naively. At this point, one could also ask, "So why not implementing it by hand, the loop is simple enough and I don't even need the return value?", which is, of course, a good question.
While std::copy is not the best example for making code significantly shorter, a lot of other algorithms with more complex implementations are. What is not obvious is the hidden automatic optimization of such STL algorithms. If we happen to use std::copy with data structures that store their items in contiguous memory (as std::vector and std::array do), and the items themselves are trivially copy assignable, then the compiler will select a completely different implementation (which assumes the iterator types to be pointers):
template <typename InputIterator, typename OutputIterator>
OutputIterator copy(InputIterator it, InputIterator end_it,
OutputIterator out_it)
{
const size_t num_items (distance(it, end_it));
memmove(out_it, it, num_items * sizeof(*it));
return it + num_items;
}
This is a simplified version of how the memmove variant of the std::copy algorithm can look in a typical STL implementation. It is faster than the standard loop version, and this time, it is also not as nice to read. But nevertheless, std::copy users automatically profit from it if their argument types comply with the requirements of this optimization. The compiler selects the fastest implementation possible for the chosen algorithm, while the user code nicely expresses what the algorithm does without tainting the code with too many details of the how.
STL algorithms often simply provide the best trade-off between readability and optimal implementation.
We also used std::move. It works exactly like std::copy, but it applies std::move(*it) to the source iterator in the loop in order to cast lvalues to rvalues. This makes the compiler select the move assignment operator of the target object instead of the copy assignment operator. For a lot of complex objects, this performs better but destroys the source object.