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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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;
}
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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;
}
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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;
}
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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;
}
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 learn about the fill and generate algorithms. Stay tuned!