Jonathan Boccara's blog

The Difference Between std::copy_backward and std::copy with Reverse Iterators

Published May 31, 2019 - 0 Comments

Daily C++

A couple of months ago, I made a talk at the ACCU conference about learning every algorithm there is in the STL. Amongst them, we covered std::copy_backward, that makes a copy of a source range to a destination range, starting from its end and working its way back to the beginning.

In the questions session at the end of the talk, attendant Oscar Forner rose an interesting point: is there any difference between performing a std::copy_backward versus performing a simple std::copy on the reverse iterators from the source collection?

Here are Oscar’s exact words:

Indeed, the two options sound sort of similar. Do you see a difference between them? Let’s find out what it is.

std::copy_backward

Here is a reminder about std::copy_backward. If you’re already familiar with this algorithm you can skip to the next section.

std::copy-backward is one of the STL algorithms that allows moving ranges around. A simple way to illustrate the point of std::copy_backward is to start from an example.

Consider the following collection containing the numbers from 1 to 10:

How can we copy the sub-range going from 1 to 5 three positions to the right inside the collection? That is, how to get from the above state to that one:

copy_backward

A option that sounds reasonable at first is to use std::copy. If we call our collection numbers, we could attempt to write:

But contrary to what this line of code looks like, it doesn’t copy the first 5 elements three positions down. Not at all. Indeed, the first thing std::copy does is to copy the first element of the source range over to the destination range. The first element in the source is 1, and the first location in the destination holds the 4:

std::copy

Huh-oh. Not good, we’ve lost the 4.

What we would like is to start copying from the end of the source range and work our way backwards. Starting with 5, the last element of the source range:

copy_backward

So we need to copy, but backwards. This is what std::copy_backward does:

Note the output iterator: it is at the end of the destination collection, since this is where std::copy_backward has to start writing its results.

After the call to std::copy_backward, the collection is in the following state:

copy_backward

So this is std::copy_backward.

Reverse iterators

The initial question was to compare std::copy_backward with using reverse iterators. So let’s leave std::copy_backward aside for a moment to make a quick recap on reverse iterators. If you’re already familiar with reverse iterators, you can skip to the next section.

The simplest way to traverse a collection is by using a pair of iterators that go from its first element to its last. In the STL containers, such as std::vector and std::map, those iterators are  accessible via the begin and end functions.

But if the structure of the collection allows an iterator to go backwards (bidirectional iterators), it can also provide reverse iterators. This is the case of almost all STL containers. For example, std::vector and std::map provide rbegin and rend.

To illustrate, consider the following program:

Its output is:

Reverse iterators offer an operator++ just like their forward counterparts, but theirs move backwards in the collection instead of forwards.

std::copy_backward VS reverse iterators

Both std::copy_backward and reverse iterators allow to traverse a collection in reverse order. Are they equivalent?

Let’s take our initial usage of std::copy_backward that took the collection from this state:

To that one:

copy_backward

Here is the full program:

It indeed outputs:

How could we write a program that achieve the same result, but with reverse iterators?

If we start from the end of the collection, the subrange to copy (the one going from 1 to 5) goes from rbegin + 5 to rbegin + 10 (which by coincidence happens to be rend in this case). So that would be our source: from rbegin + 5 to rbegin + 10.

What about the destination? If we pass a reverse iterator as an output to std::copy, then the starting point from the destination is its last element, so the one that holds 8. Indeed, std::copy applies operator++ to advance its output iterators, which effectively goes backwards into the collection, since we’re using a reverse iterator in output. And counting from the end, the position of 8 is rbegin + 2.

Here is the corresponding program:

It also outputs:

Copying forwards, copying backwards, and the reverse of the backwards

As we saw with the STL algorithms that move ranges around, to copy a sub-range further right we should use std::copy_backward, and to copy a sub-range further left we should use std::copy, which sounds kind of weird.

Now that reverse iterators enter the picture, we see that we can also copy a sub-range further right by using std::copy and reverse iterators. And, similarly, we can copy a sub-range further left with std::copy_backward and reverse iterators.

Here is an example of program that illustrate that last statement, “copying a sub-range further left with std::copy_backward and reverse iterators”:

It outputs:

We’ve copied the last 5 elements two positions left inside the collection.

It seems to mo me that using std::copy and std::copy_backward with forward iterators results in more natural code than using them with reverse iterators. But the resulting English statements may sound more logical: “we can copy a sub-range further left with std::copy_backward and reverse iterators”. What do you think?

In any case an even simpler solution would be to encapsulate everything behind a nice interface, like Dan Raviv has been proposing to the C++ committee with the shift operations.

Thanks Oscar for this great question. If, like Oscar, you’d like to discuss a topic about the STL algorithms, you can reach out to me by email at jonathan@fluentcpp.com.

You may also like

Become a Patron!
Share this post! Facebooktwittergoogle_pluslinkedin    Don't want to miss out ? Follow:   twitterlinkedinrss