The big STL Algorithms tutorial: modifying sequence operations - replace*

Sandor Dargo - Jan 29 '20 - - Dev Community

In this next part of the big STL algorithm tutorial, we will discover the 4 algorithms starting with the word replace:

  • replace
  • replace_if
  • replace_copy
  • replace_copy_if

Let's get started!

replace

There is not much surprise in this algorithm, it does what its name suggests and that's a good thing. As François-Guillaume RIBREAU said at DevOps D-Day, an API should be boring, meaning that among others its signature should be straightforward.

replace takes a range defined by the iterators pointing at the first and last elements of it, plus it takes an old value that should be replaced by the value.

The only question you might get based on the name, whether it replaces the first occurrence of the to be replaced item, or all of them. It will replace all of them. Here is an example:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> numbers { 1, 2, 3, 4, 5, 4, 7, 4, 9, 10 };

  std::replace(numbers.begin(), numbers.end(), 4, 42); 

  std::cout << "numbers after replace: ";
  for (const auto& number : numbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

But how to replace just the first (n) elements? That's a story for another day.

replace_copy

replace_copy is quite similar to replace, the difference is that it leaves the input range untouched and writes results into another container.

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> numbers { 1, 2, 3, 4, 5, 4, 7, 4, 9, 10 };
  std::vector<int> otherNumbers (numbers.size());

  std::replace_copy(numbers.begin(), numbers.end(), otherNumbers.begin(), 4, 42); 

  std::cout << "numbers after replace_copy have not changed: ";
  for (const auto& number : numbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  std::cout << "otherNumbers after replace: ";
  for (const auto& number : otherNumbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

Some important notes:

  • The output range is defined not by two, but by one iterator pointing at the first element of the output range. Don't forget the output range has to be at least as big as the input one. If not, the behaviour is undefined.
  • Not only the replaced elements are written to the output range, but every element. If you want to copy only replaced items, you have to combine two algorithms. One possibility is this:
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> numbers { 1, 2, 3, 4, 5, 4, 7, 4, 9, 10 };
  std::vector<int> otherNumbers;

  std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(otherNumbers), [](int number){return number == 4;});
  std::replace(otherNumbers.begin(), otherNumbers.end(), 4, 42); 

  std::cout << "numbers after replace have not changed: ";
  for (const auto& number : numbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  std::cout << "otherNumbers after replace: ";
  for (const auto& number : otherNumbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

replace_if

Just like replace, replace_if also takes a range defined by the iterators pointing at the first and last elements of it, then right after the range and before the new value instead of an old value it takes a unary predicate.

This result of predicate decides whether a value should be replaced or not. As usual, it can be a pointer to a function, a functor or a lambda exprssion.

Here is an example:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> numbers { 1, 2, 3, 4, 5, 4, 7, 4, 9, 10 };

  std::replace_if(numbers.begin(), numbers.end(), [](auto number){return number == 4;}, 42); 

  std::cout << "numbers after replace: ";
  for (const auto& number : numbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

replace_copy_if

Finally, let's have a quick look at replace_copy_if. I'm sure you can guess what it does and how it accepts its parameters after just having read about replace_copy and replace_if. It works the same way as replace_copy, but instead of the fourth parameter defining the old value, it accepts a unary predicate, just like replace_if.

As a reminder, a unary predicate can be a pointer to a function, a functor or a lambda exprssion. I always use the latter in my examples as they are so short.

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> numbers { 1, 2, 3, 4, 5, 4, 7, 4, 9, 10 };
  std::vector<int> otherNumbers (numbers.size());

  std::replace_copy_if(numbers.begin(), numbers.end(), otherNumbers.begin(), [](auto number){return number == 4;}, 42); 

  std::cout << "numbers after replace_copy have not changed: ";
  for (const auto& number : numbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  std::cout << "otherNumbers after replace: ";
  for (const auto& number : otherNumbers) {
    std::cout << ' ' << number;
  }
  std::cout << '\n';

  return 0;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Today, we had a peek into the algorithms replacing elements of a container. We saw that there 4 different versions depending on whether we want an in-place or a copy replace and whether we want to identify to-be-replaced elements by value or by a more elaborate condition.

We also saw that the replace* algorithms can only replace all the items matching the condition, in order to replace a given number of items, other algorithms have to be used - a topic for another blog post.

Next time we’ll start learning about the transform algorithm. Stay tuned!

This article has been originally posted on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter and follow me on Twitter.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .