16.4 Algorithms

Sections 16.4.116.4.12 demonstrate many of the Standard Library algorithms.

16.4.1 fill, fill_n, generate and generate_n

Figure 16.2 demonstrates algorithms fill, fill_n, generate and generate_n. Algorithms fill and fill_n set every element in a range of container elements to a specific value. Algorithms generate and generate_n use a generator function to create values for every element in a range of container elements. The generator function takes no arguments and returns a value that can be placed in an element of the container. In this example, we’ll define a generator function as a standalone function and as a lambda, so you can see the similarities. For the remainder of the chapter, we’ll use lambdas.

Fig. 16.2 Algorithms fill, fill_n, generate and generate_n—we used bold text to highlight the changes made to chars by each algorithm.

Alternate View

 1    // Fig. 16.2: fig16_02.cpp
 2    // Algorithms fill, fill_n, generate and generate_n.
 3    #include <iostream>
 4    #include <algorithm> // algorithm definitions
 5    #include <array> // array class-template definition
 6    #include <iterator> // ostream_iterator
 7    using namespace std;
 8
 9    // generator function returns next letter (starts with A)
10    char nextLetter() {
11       static char letter{'A'};
12       return letter++;
13   }
14
15    int main() {
16       array<char, 10> chars;
17       fill(chars.begin(), chars.end(), '5'); // fill chars with 5s
18
19       cout << "chars after filling with 5s:
";
20       ostream_iterator<char> output{cout, " "};
21       copy(chars.cbegin(), chars.cend(), output);
22
23       // fill first five elements of chars with As
24       fill_n(chars.begin(), 5, 'A');              
25
26       cout << "

chars after filling five elements with As:
";
27       copy(chars.cbegin(), chars.cend(), output);
28
29      // generate values for all elements of chars with nextLetter
30      generate(chars.begin(), chars.end(), nextLetter);           
31
32      cout << "

chars after generating letters A-J:
";
33      copy(chars.cbegin(), chars.cend(), output);
34
35      // generate values for first five elements of chars with nextLetter
36      generate_n(chars.begin(), 5, nextLetter);                          
37
38      cout << "

chars after generating K-O for the"
39        << " first five elements:
";
40      copy(chars.cbegin(), chars.cend(), output);
41      cout << endl;
42
43     // generate values for first three elements of chars with a lambda
44     generate_n(chars.begin(), 3,
45        [](){ // lambda that takes no arguments
46           static char letter{'A'};            
47           return letter++;                    
48          }                                    
49   );
50   
51    cout << "
chars after using a lambda to generate A-C "
52       << "for the first three elements:
";
53    copy(chars.cbegin(), chars.cend(), output);
54    cout << endl;
55   }

chars after filling with 5s:
5 5 5 5 5 5 5 5 5 5

chars after filling five elements with As:
A A A A A 5 5 5 5 5

chars after generating letters A-J:
A B C D E F G H I J

chars after generating K-O for the first five elements:
K L M N O F G H I J

chars after using a lambda to generate A-C for the three elements:
A B C N O F G H I J

fill Algorithm

Line 16 defines a 10-element array of char values. Line 17 uses the fill algorithm to place the character '5' in every element of chars from chars.begin() up to, but not including, chars.end(). The iterators supplied as the first and second argument must be at least forward iterators (i.e., they can be used for both input from a container and output to a container in the forward direction). Forward iterators are required here (rather than output iterators) because the iterators must be compared to determine when the end of the sequence has been reached.

fill_n Algorithm

Line 24 uses the fill_n algorithm to place the character 'A' in the first five elements of chars. The iterator supplied as the first argument must be at least an output iterator (i.e., it can be used to write into a container in the forward direction). The second argument specifies the number of elements to fill. The third argument specifies the value to place in each element.

generate Algorithm

Line 30 uses the generate algorithm to place the result of a call to generator function nextLetter in every element of chars from chars.begin() up to, but not including, chars.end(). The iterators supplied as the first and second arguments must be at least forward iterators. Function nextLetter (lines 10–13) defines a static local char variable named letter and initializes it to 'A'. The statement in line 12 returns the current value of letter then postincrements its value for use in the next call to the function.

generate_n Algorithm

Line 36 uses the generate_n algorithm to place the result of a call to generator function nextLetter in five elements of chars, starting from chars.begin(). The iterator supplied as the first argument must be at least an output iterator.

Using the generate_n Algorithm with a Lambda

Lines 44–49 once again use the generate_n algorithm to place the result of a call to a generator function into elements of chars, starting from chars.begin(). In this case, the generator function is implemented as a lambda (lines 45–48) with no arguments—as specified by the empty parentheses—and returns a generated letter. For lambdas with no arguments, the parameter lists’ paretheses are not required. The compiler infers from the return statement that the lambda’s return type is char.

A Note About Reading Standard Library Algorithm Documentation

When you look at the Standard Library algorithms documentation for algorithms that can receive function pointers as arguments, you’ll notice that the corresponding parameters do not show pointer declarations. Such parameters can actually receive as arguments function pointers, function objects (Section 16.5) or lambda expressions (Section 16.3). For this reason, the Standard Library declares such parameters using names that represent the parameter’s purpose.

For example, generate’s prototype is listed in the C++ standard document as


template<class ForwardIterator, class Generator>
void generate(ForwardIterator first, ForwardIterator last,
  Generator gen);

indicating that generate expects as arguments ForwardIterators representing the range of elements to process and a Generator function. The standard explains that the algorithm calls the Generator function to obtain a value for each element in the range specified by the ForwardIterators. The standard also specifies that the Generator must take no arguments and return a value that can be assigned to the element type.

Similar documentation is provided for each algorithm that can receive a function pointer, function object or lambda expression. In most of this chapter’s examples, as we present each algorithm, we specify the requirements for such parameters.

16.4.2 equal, mismatch and lexicographical_compare

Figure 16.3 demonstrates comparing sequences of values for equality using algorithms equal, mismatch and lexicographical_compare. Lines 11–13 create and initialize three arrays. When invoking an array’s copy constructor (line 12), you cannot use braces, as in


array<int, SIZE> a2{a1};

The preceding declaration yields a compilation error, because the compiler treats the contents in braces as a list of values for the array’s elements. In this case, the compiler attempts to initialize the first int element of a2 with the array object a1—there is no implicit conversion from an array object to a single int value.

Fig. 16.3 Algorithms equal, mismatch and lexicographical_compare.

Alternate View

 1   // Fig. 16.3: fig16_03.cpp
 2   // Algorithms equal, mismatch and lexicographical_compare.
 3   #include <iostream>
 4   #include <algorithm> // algorithm definitions
 5   #include <array> // array class-template definition
 6   #include <iterator> // ostream_iterator
 7   using namespace std;
 8
 9   int main() {
10      const size_t SIZE{10};
11      array<int, SIZE> a1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
12      array<int, SIZE> a2(a1); // initializes a2 with copy of a1
13      array<int, SIZE> a3{1, 2, 3, 4, 1000, 6, 7, 8, 9, 10};
14      ostream_iterator<int> output{cout, " "};
15
16      cout << "a1 contains: ";
17      copy(a1.cbegin(), a1.cend(), output);
18      cout << "
a2 contains: ";
19      copy(a2.cbegin(), a2.cend(), output);
20      cout << "
a3 contains: ";
21      copy(a3.cbegin(), a3.cend(), output);
22
23      // compare a1 and a2 for equality
24      bool result{equal(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend())};
25      cout << "

a1 " << (result ? "is" : "is not") << " equal to a2
";
26
27      // compare a1 and a3 for equality
28      result = equal(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cend());
29      cout <<"a1" << (result ? "is" : "is not") << " equal to a3
";
30
31      // check for mismatch between a1 and a3
32      auto location =                                             
33         mismatch(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cend());
34      cout << "
There is a mismatch between a1 and a3 at location "
35         << (location.first - a1.begin()) << "
where a1 contains "
36         << *location.first << " and a3 contains " << *location.second
37         "

";
38
39     char c1[SIZE] = "HELLO";
40     char c2[SIZE] = "BYE BYE";
41
42    // perform lexicographical comparison of c1 and c2
43    result = lexicographical_compare(              
44       cbegin(c1), cend(c1), cbegin(c2), cend(c2));
45    cout << c1 << (result?" is less than " :
46       " is greater than or equal to ") << c2 << endl;
47   }

a1 contains: 1 2 3 4 5 6 7 8 9 10
a2 contains: 1 2 3 4 5 6 7 8 9 10
a3 contains: 1 2 3 4 1000 6 7 8 9 10

a1 is equal to a2
a1 is not equal to a3

There is a mismatch between a1 and a3 at location 4
where a1 contains 5 and a3 contains 1000

HELLO is greater than or equal to BYE BYE

equal Algorithm

Line 24 uses the C++14 version of the equal algorithm to compare two sequences of values for equality. The second sequence must contain at least as many elements as the first—equal returns false if the sequences are not of the same length. The == operator (whether built-in or overloaded) performs the element comparisons. In this example, the elements in a1 from a1.cbegin() up to, but not including, a1.cend() are compared to the elements in a2 starting from a2.cbegin() up to, but not including, a2.cend(). In this example, a1 and a2 are equal. The four iterator arguments must be at least input iterators (i.e., they can be used for input from a sequence in the forward direction). Line 28 uses function equal to compare a1 and a3, which are not equal.

equal Algorithm with Binary Predicate Function

Each version of equal also has an overloaded version that takes a binary predicate function as the last parameter. The binary predicate function receives the two elements being compared and returns a bool value indicating whether the elements are equal. This can be useful in sequences that store objects or pointers to values rather than actual values, because you can define one or more comparisons. For example, you can compare Employee objects for age, social security number, or location rather than comparing entire objects. You can compare what pointers refer to rather than comparing the pointer values (i.e., the addresses stored in the pointers).

mismatch Algorithm

Lines 32–33 call the C++14 version of the mismatch algorithm to compare two sequences of values. The algorithm returns a pair of iterators indicating the location in each sequence of the first mismatched elements. If all the elements match, the two iterators in the pair are equal to the end iterator for each sequence. The four iterator arguments must be at least input iterators. We infer the type of the pair object location with C++11’s auto keyword (line 32). Line 35 determines the actual location of the mismatch in the arrays with the expression location.first - a1.begin(), which evaluates to the number of elements between the iterators in the returned pair (this is analogous to pointer arithmetic; Chapter 8). This corresponds to the element number in this example, because the comparison is performed from the beginning of each array. As with equal, there are overloaded versions of mismatch take a binary predicate function as the last parameter.

Error-Prevention Tip 16.1

Always use C++14’s versions of equal and mismatch. Prior to C++14, these algorithms each received only three iterators—the third indicated the starting point in the second of the two sequences being compared. The programmer was required to test whether the two sequences were of the same length before calling these algorithms. The C++14 versions are preferred because they compare the lengths of the ranges for you, eliminating a potential source of logic errors.

auto Variables and List Initialization

Lines 32–33 use an equal sign (=) rather than braces to initialize the variable location. This is due to a known limitation with variables that are declared auto and initialized with braced initializers. There is a proposal to fix this limitation in C++17.1

Error-Prevention Tip 16.2

In C++14, use = rather than braces when initializing a variable declared with auto.

lexicographical_compare Algorithm

Lines 43–44 use the lexicographical_compare algorithm to compare the contents of two char built-in arrays. This algorithm’s four iterator arguments must be at least input iterators. As you know, pointers into built-in arrays are random-access iterators. The first two iterator arguments specify the range of locations in the first sequence. The last two specify the range of locations in the second sequence. Once again, we use the C++11 begin and end functions to determine the range of elements for each built-in array. While iterating through the sequences, if there is a mismatch between the corresponding elements in the sequences and the element in the first sequence is less than the corresponding element in the second sequence, the algorithm returns true; otherwise, the algorithm returns false. This algorithm can be used to arrange sequences lexicographically. Typically, such sequences contain strings.

16.4.3 remove, remove_if, remove_copy and remove_copy_if

Figure 16.4 demonstrates removing values from a sequence with algorithms remove, remove_if, remove_copy and remove_copy_if.

Fig. 16.4 Algorithms remove, remove_if, remove_copy and remove_copy_if.

Alternate View

 1   // Fig. 16.4: fig16_04.cpp
 2   // Algorithms remove, remove_if, remove_copy and remove_copy_if.
 3   #include <iostream>
 4   #include <algorithm> // algorithm definitions
 5   #include <array> // array class-template definition
 6   #include <iterator> // ostream_iterator
 7   using namespace std;
 8
 9   int main() {
10      const size_t SIZE{10};
11      array<int, SIZE> init{10, 2, 10, 4, 16, 6, 14, 8, 12, 10};
12      ostream_iterator<int> output{cout, " "};
13
14      array<int, SIZE> a1(init); // initialize with copy of init
15      cout << "a1 before removing all 10s:
 ";
16      copy(a1.cbegin(), a1.cend(), output);
17
18      // remove all 10s from a1
19      auto newEnd = remove(a1.begin(), a1.end(), 10);
20      cout << "
a1 after removing all 10s:
 ";
21      copy(a1.begin(), newEnd, output);
22
23      array<int, SIZE> a2(init); // initialize with copy of init
24      array<int, SIZE> c{0}; // initialize to 0s
25      cout << "

a2 before removing all 10s and copying:
 ";
26      copy(a2.cbegin(), a2.cend(), output);
27
28      // copy from a2 to c, removing 10s in the process
29      remove_copy(a2.cbegin(), a2.cend(), c.begin(), 10);
30      cout << "
a2 after removing all 10s from a2:
 ";
31      copy(c.cbegin(), c.cend(), output);
32
33      array<int, SIZE> a3(init); // initialize with copy of init
34      cout << "

a3 before removing all elements greater than 9:
 ";
35      copy(a3.cbegin(), a3.cend(), output);
36
37     // remove elements greater than 9 from a3
38     newEnd = remove_if(a3.begin(), a3.end(),
39       [](auto x){return x > 9;});           
40     cout << "
a3 after removing all elements greater than 9:
 ";
41     copy(a3.begin(), newEnd, output);
42
43     array<int, SIZE> a4(init); // initialize with copy of init
44     array<int, SIZE> c2{0}; // initialize to 0s
45     cout << "

a4 before removing all elements "
46       << "greater than 9 and copying:
 ";
47     copy(a4.cbegin(), a4.cend(), output);
48
49    // copy elements from a4 to c2, removing elements greater
50    // than 9 in the process
51    remove_copy_if(a4.cbegin(), a4.cend(), c2.begin(),
52       [](auto x){return x > 9;});                    
53    cout << "
c2 after removing all elements "
54      << "greater than 9 from a4:
 ";
55    copy(c2.cbegin(), c2.cend(), output);
56    cout << endl;
57   }

a1 before removing all 10s:
   10 2 10 4 16 6 14 8 12 10
a1 after removing all 10s:
   2 4 16 6 14 8 12

a2 before removing all 10s and copying:
   10 2 10 4 16 6 14 8 12 10
c after removing all 10s from a2:
  2 4 16 6 14 8 12 0 0 0

a3 before removing all elements greater than 9:
   10 2 10 4 16 6 14 8 12 10
a3 after removing all elements greater than 9:
   2 4 6 8

a4 before removing all elements greater than 9 and copying:
   10 2 10 4 16 6 14 8 12 10
c2 after removing all elements greater than 9 from a4:
   2 4 6 8 0 0 0 0 0 0

remove Algorithm

Line 19 uses the remove algorithm to eliminate from a1 all elements with the value 10 in the range from a1.begin() up to, but not including, a1.end(). The first two iterator arguments must be forward iterators. This algorithm does not modify the number of elements in the container or destroy the eliminated elements, but it does move all elements that are not eliminated toward the beginning of the container. The algorithm returns an iterator positioned after the last element that was not removed. Elements from the iterator position to the end of the container have unspecified values and should not be used other than to assign them new values. Line 21 outputs the elements of a1 from a1.begin() up to but not including newEnd.

remove_copy Algorithm

Line 29 uses the remove_copy algorithm to copy all elements from a2 that do not have the value 10 in the range from a2.cbegin() up to, but not including, a2.cend(). The elements are placed in c, starting at position c.begin(). The iterators supplied as the first two arguments must be input iterators. The iterator supplied as the third argument must be an output iterator so that the element being copied can be written into into the destination container. This algorithm returns an iterator positioned after the last element copied into c. Line 31 outputs all of c’s elements.

remove_if Algorithm

Lines 38–39 use the remove_if algorithm to delete from a3 all those elements in the range from a3.begin() up to, but not including, a3.end() for which a unary predicate function (in this case, the lambda at line 39) returns true—a unary predicate function must receive one parameter and return a bool value. The lambda


[](auto x){return x > 9;}

returns true if the value passed to it is greater than 9; otherwise, the lambda returns false. The compiler uses type inference for both the lambda’s parameter and return types:

  • We’re processing an array of ints, so the compiler infers the lambda’s parameter type as int.

  • The lambda returns a condition’s result, so the compiler infers the lambda’s return type as bool.

The iterators supplied as the first two arguments must be forward iterators. This algorithm does not modify the number of elements in the container, but it does move to the beginning of the container all elements that are not removed. This algorithm returns an iterator positioned after the last element that was not removed. All elements from the iterator position to the end of the container have undefined values and should not be used. Line 41 outputs the elements of a3 from a3.begin() up to but not including newEnd.

remove_copy_if Algorithm

Lines 51–52 use the remove_copy_if algorithm to copy the elements from a4 in the range from a4.cbegin() up to, but not including, a4.cend() for which a unary predicate function (the lambda at line 52) returns true. The elements are placed in the array c2, starting at c2.begin(). The iterators supplied as the first two arguments must be input iterators. The iterator supplied as the third argument must be an output iterator so that the element being copied can be written into to the destination container. This algorithm returns an iterator positioned after the last element copied into c2. Line 55 outputs the entire contents of c2.

16.4.4 replace, replace_if, replace_copy and replace_copy_if

Figure 16.5 demonstrates replacing values from a sequence using algorithms replace, replace_if, replace_copy and replace_copy_if.

Fig. 16.5 Algorithms replace, replace_if, replace_copy and replace_copy_if.

Alternate View

 1   // Fig. 16.5: fig16_05.cpp
 2   // Algorithms replace, replace_if, replace_copy and replace_copy_if.
 3   #include <iostream>
 4   #include <algorithm>
 5   #include <array>
 6   #include <iterator> // ostream_iterator
 7   using namespace std;
 8
 9   int main() {
10      const size_t SIZE{10};
11      array<int, SIZE> init{10, 2, 10, 4, 16, 6, 14, 8, 12, 10};
12      ostream_iterator<int> output{cout, " "};
13
14      array<int, SIZE> a1(init); // initialize with copy of init
15      cout << "a1 before replacing all 10s:
 ";
16      copy(a1.cbegin(), a1.cend(), output);
17
18     // replace all 10s in a1 with 100
19     replace(a1.begin(), a1.end(), 10, 100);
20     cout << "
a1 after replacing 10s with 100s:
 ";
21     copy(a1.cbegin(), a1.cend(), output);
22
23     array<int, SIZE> a2(init); // initialize with copy of init
24     array<int, SIZE> c1; // instantiate c1
25     cout << "

a2 before replacing all 10s and copying:
 ";
26     copy(a2.cbegin(), a2.cend(), output);
27
28     // copy from a2 to c1, replacing 10s with 100s
29     replace_copy(a2.cbegin(), a2.cend(), c1.begin(), 10, 100);
30     cout << "
c1 after replacing all 10s in a2:
 ";
31     copy(c1.cbegin(), c1.cend(), output);
32
33     array<int, SIZE> a3(init); // initialize with copy of init
34     cout << "

a3 before replacing values greater than 9:
 ";
35     copy(a3.cbegin(), a3.cend(), output);
36
37     // replace values greater than 9 in a3 with 100
38     replace_if(a3.begin(), a3.end(), [](auto x){return x > 9;}, 100);
39     cout <<"
a3 after replacing all values greater "
40        << "than 9 with 100s:
 ";
41     copy(a3.cbegin(), a3.cend(), output);
42
43     array<int, SIZE> a4(init); // initialize with copy of init
44     array<int, SIZE> c2; // instantiate c2‘
45     cout << "

a4 before replacing all values greater "
46        << "than 9 and copying:
 ";
47     copy(a4.cbegin(), a4.cend(), output);
48
49     // copy a4 to c2, replacing elements greater than 9 with 100
50     replace_copy_if(a4.cbegin(), a4.cend(), c2.begin(),
51        [](auto x){return x > 9;}, 100);                
52     cout <<"
c2 after replacing all values greater than 9 in a4:
 ";
53     copy(c2.begin(), c2.end(), output);
54     cout << endl;
55   }

a1 before replacing all 10s:
   10 2 10 4 16 6 14 8 12 10
a1 after replacing 10s with 100s:
   100 2 100 4 16 6 14 8 12 100

a2 before replacing all 10s and copying:
   10 2 10 4 16 6 14 8 12 10
c1 after replacing all 10s in a2:
   100 2 100 4 16 6 14 8 12 100

a3 before replacing values greater than 9:
   10 2 10 4 16 6 14 8 12 10
a3 after replacing all values greater than 9 with 100s:
   100 2 100 4 100 6 100 8 100 100

a4 before replacing all values greater than 9 and copying:
   10 2 10 4 16 6 14 8 12 10
c2 after replacing all values greater than 9 in a4:
   100 2 100 4 100 6 100 8 100 100

replace Algorithm

Line 19 uses the replace algorithm to replace all elements with the value 10 in the range a1.begin() up to, but not including, a1.end() with the new value 100. The iterators supplied as the first two arguments must be forward iterators so that the algorithm can modify the elements in the sequence.

replace_copy Algorithm

Line 29 uses the replace_copy algorithm to copy all elements in the range a2.cbegin() up to, but not including, a2.cend(), replacing all elements with the value 10 with the new value 100. The elements are copied into c1, starting at position c1.begin(). The iterators supplied as the first two arguments must be input iterators. The iterator supplied as the third argument must be an output iterator so that the element being copied can be written into to the destination container. This function returns an iterator positioned after the last element copied into c1.

replace_if Algorithm

Line 38 uses the replace_if algorithm to replace all those elements from a3.begin() up to, but not including, a3.end() for which a unary predicate function returns true—the lambda specified as the third argument returns true if the value passed to it is greater than 9; otherwise, it returns false. The value 100 replaces each value greater than 9. The iterators supplied as the first two arguments must be forward iterators.

replace_copy_if Algorithm

Lines 50–51 use the replace_copy_if algorithm to copy all elements from a4.cbegin() up to, but not including, a4.cend(). Elements for which a unary predicate function returns true—again for values greater than 9—are replaced with the value 100. The elements are placed in c2, starting at position c2.begin(). The iterators supplied as the first two arguments must be input iterators. The iterator supplied as the third argument must be an output iterator so that the element being copied can be written into to the destination container. This algorithm returns an iterator positioned after the last element copied into c2.

16.4.5 Mathematical Algorithms

Figure 16.6 demonstrates several common mathematical algorithms, including shuffle, count, count_if, min_element, max_element, minmax_element, accumulate and transform. We already introduced the for_each mathematical algorithm in Section 16.3.

Fig. 16.6 Mathematical algorithms of the Standard Library.

Alternate View

 1   // Fig. 16.6: fig16_06.cpp
 2   // Mathematical algorithms of the Standard Library.
 3   #include <iostream>
 4   #include <algorithm> // algorithm definitions 
 5   #include <numeric> // accumulate is defined here
 6   #include <array>
 7   #include <iterator>
 8   #include <random> // contains C++11 random number generation features
 9   using namespace std;
10
11   int main() {
12      const size_t SIZE{10};
13      array<int, SIZE> a1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
14      ostream_iterator<int> output{cout, " "};
15
16      cout << "a1 before random_shuffle: ";
17      copy(a1.cbegin(), a1.cend(), output);
18
19      // create random-number engine and use it to help shuffle a1's elements
20      default_random_engine randomEngine{random_device{}()};                 
21      shuffle(a1.begin(), a1.end(), randomEngine); // randomly order elements
22      cout << "
a1 after random_shuffle: ";
23      copy(a1.cbegin(), a1.cend(), output);
24
25      array<int, SIZE> a2{100, 2, 8, 1, 50, 3, 8, 8, 9, 10};
26      cout << "

a2 contains: ";
27      copy(a2.cbegin(), a2.cend(), output);
28
29      // count number of elements in a2 with value 8
30      auto result = count(a2.cbegin(), a2.cend(), 8);
31     cout << "
Number of elements matching 8: " << result;
32
33     // count number of elements in a2 that are greater than 9
34     result = count_if(a2.cbegin(), a2.cend(), [](auto x){return x > 9;});
35     cout << "
Number of elements greater than 9: " << result;
36
37     // locate minimum element in a2
38     cout << "

Minimum element in a2 is: "
39        << *(min_element(a2.cbegin(), a2.cend()));
40
41     // locate maximum element in a2
42     cout << "
Maximum element in a2 is: "
43       << *(max_element(a2.cbegin(), a2.cend()));
44
45     // locate minimum and maximum elements in a2
46     auto minAndMax = minmax_element(a2.cbegin(), a2.cend());
47     cout << "
The minimum and maximum elements in a2 are "
48       << *minAndMax.first << " and " << *minAndMax.second
49       << ", respectively";
50
51     // calculate sum of elements in a1
52     cout << "

The total of the elements in a1 is: "
53       << accumulate(a1.cbegin(), a1.cend(), 0);
54
55     array<int, SIZE> cubes; // instantiate cubes
56
57     // calculate cube of each element in a1; place results in cubes
58     transform(a1.cbegin(), a1.cend(), cubes.begin(),
59        [](auto x){return x * x * x;});              
60     cout << "

The cube of every integer in a1 is:
";
61     copy(cubes.cbegin(), cubes.cend(), output);
62     cout << endl;
63   }

a1 before random_shuffle: 1 2 3 4 5 6 7 8 9 10
a1 after random_shuffle: 9 2 10 3 1 6 8 4 5 7

a2 contains: 100 2 8 1 50 3 8 8 9 10
Number of elements matching 8: 3
Number of elements greater than 9: 3

Minimum element in a2 is: 1
Maximum element in a2 is: 100
The minimum and maximum elements in a2 are 1 and 100, respectively

The total of the elements in a1 is: 55

The cube of every integer in a1 is:
729 8 1000 27 1 216 512 64 125 343

shuffle Algorithm

Line 21 uses the C++11 shuffle algorithm to reorder randomly the elements in the range a1.begin() up to, but not including, a1.end(). This algorithm takes two random-access iterator arguments and a C++11 random-number-generator engine. Line 20


default_random_engine randomEngine{random_device{}()};

creates a default_random_engine using a C++11 random_device object to seed the random-number generator—typically with a nondeterministic seed (i.e., a seed that cannot be predicted). In the expression


random_device{}()

the braces initialize the random_device object and the parentheses call its overloaded parentheses operator to get the seed. Line 23 displays the shuffled results.

count Algorithm

Line 30 uses the count algorithm to count the elements with the value 8 in the range a2.cbegin() up to, but not including, a2.cend(). This algorithm requires its two iterator arguments to be at least input iterators.

count_if Algorithm

Line 34 uses the count_if algorithm to count elements in the range from a2.cbegin() up to, but not including, a2.cend() for which a unary predicate function returns true—once again we used a lambda to define a unary predicate that returns true for a value greater than 9. Algorithm count_if requires its two iterator arguments to be at least input iterators.

min_element Algorithm

Line 39 uses the min_element algorithm to locate the smallest element in the range from a2.cbegin() up to, but not including, a2.cend(). The algorithm returns a forward iterator located at the first smallest element, or a2.end() if the range is empty. The algorithm’s two iterator arguments must be at least forward iterators. An overloaded version of this algorithm receives as its third argument a binary predicate function that compares two elements in the sequence and returns the bool value true if the first argument is less than the second.

max_element Algorithm

Line 43 uses the max_element algorithm to locate the largest element in the range from a2.cbegin() up to, but not including, a2.cend(). The algorithm returns a forward iterator located at the first largest element. The algorithm’s two iterator arguments must be at least forward iterators. An overloaded version of this algorithm receives as its third argument a binary predicate function that compares two elements in the sequence and returns the bool value true if the first argument is less than the second.

Error-Prevention Tip 16.3

Check that the range specified in a call to min_element or max_element is not empty and that the return value is not the “past the end” iterator.

C++11: minmax_element Algorithm

Line 46 uses the C++11 minmax_element algorithm to locate both the smallest and largest elements in the range from a2.cbegin() up to, but not including, a2.cend(). The algorithm returns a pair of forward iterators located at the smallest and largest elements, respectively. If there are duplicate smallest or largest elements, the iterators are located at the first smallest and last largest values. The algorithm’s two iterator arguments must be at least forward iterators. A second version of this algorithm takes as its third argument a binary predicate function that compares the elements in the sequence. The binary function takes two arguments and returns the bool value true if the first argument is less than the second.

accumulate Algorithm

Line 53 uses the accumulate algorithm (the template of which is in header <numeric>) to sum the values in the range from a1.cbegin() up to, but not including, a1.cend(). The algorithm’s two iterator arguments must be at least input iterators and its third argument represents the initial value of the total. A second version of this algorithm takes as its fourth argument a general function that determines how elements are accumulated. The general function must take two arguments and return a result. The first argument to this function is the current value of the accumulation. The second argument is the value of the current element in the sequence being accumulated. The function returns the result of the accumulation.

transform Algorithm

Lines 58–59 use the transform algorithm to apply a general function to every element in the range from a1.cbegin() up to, but not including, a1.cend(). The general function (the fourth argument) should take the current element as an argument, must not modify the element and should return the transformed value. Algorithm transform requires its first two iterator arguments to be at least input iterators and its third argument to be at least an output iterator. The third argument specifies where the transformed values should be placed. Note that the third argument can equal the first.

An overloaded version of transform accepts five arguments—the first two arguments are input iterators that specify a range of elements from one source container, the third argument is an input iterator that specifies the first element in another source container, the fourth argument is an output iterator that specifies where the transformed values should be placed and the last argument is a general function that takes two arguments and returns a result. This version of transform takes one element from each of the two input sources and applies the general function to that pair of elements, then places the transformed value at the location specified by the fourth argument.

16.4.6 Basic Searching and Sorting Algorithms

Figure 16.7 demonstrates some basic searching and sorting Standard Library algorithms, including find, find_if, sort, binary_search, all_of, any_of, none_of and find_if_not.

Fig. 16.7 Standard Library search and sort algorithms.

Alternate View

  1   // Fig. 16.7: fig16_07.cpp
  2   // Standard Library search and sort algorithms.
  3   #include <iostream>
  4   #include <algorithm> // algorithm definitions
  5   #include <array> // array class-template definition
  6   #include <iterator>
  7   using namespace std;
  8
  9   int main() {
 10      const size_t SIZE{10};
 11      array<int, SIZE> a{10, 2, 17, 5, 16, 8, 13, 11, 20, 7};
 12      ostream_iterator<int> output{cout, " "};
 13
 14      cout << "array a contains: ";
 15      copy(a.cbegin(), a.cend(), output); // display output vector
 16
 17      // locate first occurrence of 16 in a
 18      auto location = find(a.cbegin(), a.cend(), 16);
 19
 20      if (location != a.cend()) { // found 16
 21         cout << "

Found 16 at location " << (location - a.cbegin());
 22      }
 23      else { // 16 not found
 24         cout << "

16 not found";
 25      }
 26
 27      // locate first occurrence of 100 in a
 28      location = find(a.cbegin(), a.cend(), 100);
 29
 30      if (location != a.cend()) { // found 100
 31         cout << "
Found 100 at location " << (location - a.cbegin());
 32      }
 33     else { // 100 not found
 34        cout << "
100 not found";
 35     }
 36
 37     // create variable to store lambda for reuse later
 38     auto isGreaterThan10{[](auto x){return x > 10;}};
 39
 40     // locate first occurrence of value greater than 10 in a
 41     location = find_if(a.cbegin(), a.cend(), isGreaterThan10);
 42
 43     if (location != a.cend()) { // found value greater than 10
 44        cout << "

The first value greater than 10 is " << *location
 45           << "
found at location " << (location - a.cbegin());
 46     }
 47     else { // value greater than 10 not found
 48        cout << "

No values greater than 10 were found";
 49     }
 50
 51     // sort elements of a
 52     sort(a.begin(), a.end());
 53     cout << "

array a after sort: ";
 54     copy(a.cbegin(), a.cend(), output);
 55     
 56     // use binary_search to check whether 13 exists in a
 57     if (binary_search(a.cbegin(), a.cend(), 13)) {
 58        cout << "

13 was found in a";
 59     }
 60     else {
 61       cout << "

13 was not found in a";
 62     }
 63
 64     // use binary_search to check whether 100 exists in a
 65     if (binary_search(a.cbegin(), a.cend(), 100)) {
 66        cout << "
100 was found in a";
 67     }
 68     else {
 69        cout << "
100 was not found in a";
 70     }
 71
 72     // determine whether all of the elements of a are greater than 10
 73     if (all_of(a.cbegin(), a.cend(), isGreaterThan10)) {
 74        cout << "

All the elements in a are greater than 10";
 75     }
 76     else {
 77        cout << "

Some elements in a are not greater than 10";
 78     }
 79
 80     // determine whether any of the elements of a are greater than 10
 81     if (any_of(a.cbegin(), a.cend(), isGreaterThan10)) {
 82        cout << "

Some of the elements in a are greater than 10";
 83     }
 84     else {
 85        cout << "

None of the elements in a are greater than 10";
 86     }
 87
 88     // determine whether none of the elements of a are greater than 10
 89     if (none_of(a.cbegin(), a.cend(), isGreaterThan10)) {
 90        cout << "

None of the elements in a are greater than 10";
 91     }
 92     else {
 93        cout << "

Some of the elements in a are greater than 10";
 94     }
 95
 96     // locate first occurrence of value that's not greater than 10 in a
 97     location = find_if_not(a.cbegin(), a.cend(), isGreaterThan10);
 98
 99     if (location != a.cend()) { // found a value less than or equal to 10
100        cout << "

The first value not greater than 10 is " << *location
101           << "
found at location " << (location - a.cbegin());
102     }
103     else { // no values less than or equal to 10 were found
104        cout << "

Only values greater than 10 were found";
105     }
106
107     cout << endl;
108   }

array a contains: 10 2 17 5 16 8 13 11 20 7

Found 16 at location 4
100 not found

The first value greater than 10 is 17
found at location 2

array a after sort: 2 5 7 8 10 11 13 16 17 20

13 was found in a
100 was not found in a

Some elements in a are not greater than 10

Some of the elements in a are greater than 10

Some of the elements in a are greater than 10

The first value not greater than 10 is 2
found at location 0

find Algorithm

Line 18 uses the find algorithm to locate the value 16 in the range from a.cbegin() up to, but not including, a.cend(). The algorithm requires its two iterator arguments to be at least input iterators and returns an input iterator that’s either positioned at the first element containing the value or indicates the end of the sequence (as is the case in line 28).

Storing Lambdas in Variables

Throughout this example, we test multiple times whether elements in the array a are greater than 10. You can store a lambda in a variable, as in line 38:


auto isGreaterThan10 = [](auto x){return x > 10;};

This variable can then be used at a later time to pass the lambda to other functions, as in lines 41, 73, 81, 89 and 97. You can also use the variable like a function name to invoke the lambda, as in


isGreaterThan10(5)

which returns false.

find_if Algorithm

Line 41 uses the find_if algorithm (a linear search) to locate the first value in the range from a.cbegin() up to, but not including, a.cend() for which a unary predicate function returns true—in this case, the unary predicate function is the lambda


[](auto x){return x > 10;}

that’s stored in the variable isGreaterThan10. The compiler infers parameter x’s type as int (because the array stores ints) and infers the return type as bool because the lambda returns the value of a condition. Algorithm find_if requires its two iterator arguments to be at least input iterators. The algorithm returns an input iterator that either is positioned at the first element containing a value for which the predicate function returns true or indicates the end of the sequence.

sort Algorithm

Line 52 uses the sort algorithm to arrange the elements in the range from a.begin() up to, but not including, a.end() in ascending order. This algorithm requires its two iterator arguments to be random-access iterators—so the algorithm can be used with built-in arrays and the Standard Library containers array, vector and deque. An overloaded version of this algorithm has a third argument that’s a binary predicate function taking two arguments and returning a bool indicating the sorting order. The predicate compares two values from the sequence being sorted. If the return value is true, the two elements are already in sorted order; otherwise, the two elements need to be reordered in the sequence.

binary_search Algorithm

Line 57 uses the binary_search algorithm to determine whether the value 13 is in the range from a.cbegin() up to, but not including, a.cend(). The values must be sorted in ascending order. Algorithm binary_search requires its two iterator arguments to be at least forward iterators. The algorithm returns a bool indicating whether the value was found in the sequence. Line 65 demonstrates a call to binary_search in which the value is not found. An overloaded version of this algorithm receives as a fourth argument a binary predicate function with two arguments that are values in the sequence and returning a bool. The predicate function returns true if the two elements being compared are in sorted order. If you need to know the location of the search key in the container, use the lower_bound or find algorithms rather than binary_search.

C++11: all_of Algorithm

Line 73 uses the all_of algorithm to determine whether the unary predicate function (the lambda stored in isGreaterThan10) returns true for all of the elements in the range from a.cbegin() up to, but not including, a.cend(). Algorithm all_of requires its two iterator arguments to be at least input iterators.

C++11: any_of Algorithm

Line 81 uses the any_of algorithm to determine whether the unary predicate function (the lambda stored in isGreaterThan10) returns true for at least one of the elements in the range from a.cbegin() up to, but not including, a.cend(). Algorithm any_of requires its two iterator arguments to be at least input iterators.

C++11: none_of Algorithm

Line 89 uses the none_of algorithm to determine whether the unary predicate function (the lambda stored in isGreaterThan10) returns false for all of the elements in the range from a.cbegin() up to, but not including, a.cend(). Algorithm none_of requires its two iterator arguments to be at least input iterators.

C++11: find_if_not Algorithm

Line 97 uses the find_if_not algorithm to locate the first value in the range from a.cbegin() up to, but not including, a.cend() for which the unary predicate function (the lambda stored in isGreaterThan10) returns false. Algorithm find_if requires its two iterator arguments to be at least input iterators. The algorithm returns an input iterator that either is positioned at the first element containing a value for which the predicate function returns false or indicates the end of the sequence.

16.4.7 swap, iter_swap and swap_ranges

Figure 16.8 demonstrates algorithms swap, iter_swap and swap_ranges for swapping elements.

Fig. 16.8 Algorithms swap, iter_swap and swap_ranges.

Alternate View

 1   // Fig. 16.8: fig16_08.cpp
 2   // Algorithms swap, iter_swap and swap_ranges.
 3   #include <iostream>
 4   #include <array>
 5   #include <algorithm> // algorithm definitions
 6   #include <iterator>
 7   using namespace std;
 8
 9   int main() {
10      const size_t SIZE{10};
11      array<int, SIZE> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
12      ostream_iterator<int> output{cout, " "};
13
14      cout << "Array a contains:
 ";
15      copy(a.cbegin(), a.cend(), output); // display array a
16
17      swap(a[0], a[1]); // swap elements at locations 0 and 1 of a
18
19      cout << "
Array a after swapping a[0] and a[1] using swap:
 ";
20      copy(a.cbegin(), a.cend(), output); // display array a
21
22     // use iterators to swap elements at locations 0 and 1 of array a
23     iter_swap(a.begin(), a.begin() + 1); // swap with iterators
24     cout << "
Array a after swapping a[0] and a[1] using iter_swap:
 ";
25     copy(a.cbegin(), a.cend(), output);
26
27     // swap elements in first five elements of array a with
28     // elements in last five elements of array a
29     swap_ranges(a.begin(), a.begin() + 5, a.begin() + 5 );
30
31     cout << "
Array a after swapping the first five elements "
32        << "with the last five elements:
 ";
33     copy(a.cbegin(), a.cend(), output);
34     cout << endl;
35   }

Array a contains:
   1 2 3 4 5 6 7 8 9 10
Array a after swapping a[0] and a[1] using swap:
   2 1 3 4 5 6 7 8 9 1
Array a after swapping a[0] and a[1] using iter_swap:
   1 2 3 4 5 6 7 8 9 10
Array a after swapping the first five elements with the last five elements:
   6 7 8 9 10 1 2 3 4 5

swap Algorithm

Line 17 uses the swap algorithm to exchange two values. In this example, the first and second elements of array a are exchanged. The function takes as arguments references to the two values being exchanged.

iter_swap Algorithm

Line 23 uses function iter_swap to exchange the two elements. The function takes two forward iterator arguments (in this case, iterators to elements of an array) and exchanges the values in the elements to which the iterators refer.

swap_ranges Algorithm

Line 29 uses function swap_ranges to exchange the elements from a.begin() up to, but not including, a.begin() + 5 with the elements beginning at position a.begin() + 5. The function requires three forward iterator arguments. The first two arguments specify the range of elements in the first sequence that will be exchanged with the elements in the second sequence starting from the iterator in the third argument. In this example, the two sequences of values are in the same array, but the sequences can be from different arrays or containers. The sequences must not overlap. The destination sequence must be large enough to contain all the elements of the ranges being swapped.

16.4.8 copy_backward, merge, unique and reverse

Figure 16.9 demonstrates algorithms copy_backward, merge, unique and reverse.

Fig. 16.9 Algorithms copy_backward, merge, unique and reverse.

Alternate View

 1   // Fig. 16.9: fig16_09.cpp
 2   // Algorithms copy_backward, merge, unique and reverse.
 3   #include <iostream>
 4   #include <algorithm> // algorithm definitions
 5   #include <array> // array class-template definition
 6   #include <iterator> // ostream_iterator
 7   using namespace std;
 8
 9   int main() {
10      const size_t SIZE{5};
11      array<int, SIZE> a1{1, 3, 5, 7, 9};
12      array<int, SIZE> a2{2, 4, 5, 7, 9};
13      ostream_iterator<int> output{cout, " "};
14
15      cout << "array a1 contains: ";
16      copy(a1.cbegin(), a1.cend(), output); // display a1
17      cout << "
array a2 contains: ";
18      copy(a2.cbegin(), a2.cend(), output); // display a2
19
20      array<int, SIZE> results;
21
22      // place elements of a1 into results in reverse order
23      copy_backward(a1.cbegin(), a1.cend(), results.end());
24      cout << "

After copy_backward, results contains: ";
25      copy(results.cbegin(), results.cend(), output);
26
27      array<int, SIZE + SIZE> results2;
28
29     // merge elements of a1 and a2 into results2 in sorted order
30     merge(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend(),
31        results2.begin());                                
32
33     cout << "

After merge of a1 and a2 results2 contains: ";
34     copy(results2.cbegin(), results2.cend(), output);
35
36     // eliminate duplicate values from results2
37     auto endLocation = unique(results2.begin(), results2.end());
38
39     cout << "

After unique results2 contains: ";
40     copy(results2.begin(), endLocation, output);
41
42     cout << "

array a1 after reverse: ";
43     reverse(a1.begin(), a1.end()); // reverse elements of a1
44     copy(a1.cbegin(), a1.cend(), output);
45     cout << endl;
46   }

array a1 contains: 1 3 5 7 9
array a2 contains: 2 4 5 7 9

After copy_backward, results contains: 1 3 5 7 9

After merge of a1 and a2 results2 contains: 1 2 3 4 5 5 7 7 9 9

After unique results2 contains: 1 2 3 4 5 7 9

array a1 after reverse: 9 7 5 3 1

copy_backward Algorithm

Line 23 uses the copy_backward algorithm to copy elements in the range from a1.cbegin() up to, but not including, a1.cend(), placing the elements in results by starting from the element before results.end() and working toward the beginning of the array. The algorithm returns an iterator positioned at the last element copied into the results (i.e., the beginning of results, because of the backward copy). Though they’re copied in reverse order, the elements are placed in results in the same order as a1. This algorithm requires three bidirectional iterator arguments (iterators that can be incremented and decremented to iterate forward and backward through a sequence, respectively).

One difference between copy_backward and copy is that the iterator returned from copy is positioned after the last element copied and the one returned from copy_backward is positioned at the last element copied (i.e., the first element in the sequence). Also, copy_backward can manipulate overlapping ranges of elements in a container as long as the first element to copy is not in the destination range of elements.

In addition to copy and copy_backward, C++11’s move and move_backward algorithms use move semantics (discussed in Chapter 24, C++11 and C++14 Additional Features) to move, rather than copy, objects from one container to another.

merge Algorithm

Lines 30–31 use the merge algorithm to combine two sorted ascending sequences of values into a third sorted ascending sequence. The algorithm requires five iterator arguments. The first four must be at least input iterators and the last must be at least an output iterator. The first two arguments specify the range of elements in the first sorted sequence (a1), the second two arguments specify the range of elements in the second sorted sequence (a2) and the last argument specifies the starting location in the third sequence (results2) where the elements will be merged. A second version of this algorithm takes as its sixth argument a binary predicate function that specifies the sorting order by comparing its two arguments and returning true if the first is less than the second.

back_inserter, front_inserter and inserter Iterator Adapters

Line 27 created the array results2 with the total number of elements in both a1 and a2. Using the merge algorithm requires that the sequence where the results are stored be at least the sum of the sizes of the sequences being merged. If you do not want to allocate the number of elements for the resulting sequence before the merge operation, you can use a dynamically growable vector and the following statements:


vector<int> results;
merge(a1.begin(), a1.end(), a2.begin(), a2.end(),
   back_inserter(results));

The argument back_inserter(results) uses the function template back_inserter (header <iterator>) to call the container’s default push_back function to insert an element at the end of the container—results in this case—rather than replacing an existing element’s value. If an element is inserted into a container that has no more space available, the container grows to accomodate the new element (we used a vector here, because arrays are fixed size). Thus, the number of elements in the container does not have to be known in advance.

There are two other inserters—front_inserter (uses push_front to insert an element at the beginning of a container specified as its argument) and inserter (uses insert to insert an element at the iterator supplied as its second argument in the container supplied as its first argument).

unique Algorithm

Line 37 uses the unique algorithm on the sorted sequence of elements in the range from results2.begin() up to, but not including, results2.end(). After this algorithm is applied to a sorted sequence with duplicate values, only a single copy of each value remains in the sequence. The algorithm takes two arguments that must be at least forward iterators. The algorithm returns an iterator positioned after the last element in the sequence of unique values. The values of all elements in the container after the last unique value are undefined and should not be used. An overloaded version of this algorithm receives as a third argument a binary predicate function specifying how to compare two elements for equality.

reverse Algorithm

Line 43 uses the reverse algorithm to reverse all the elements in the range from a1.begin() up to, but not including, a1.end(). The algorithm takes two arguments that must be at least bidirectional iterators.

C++11: copy_if and copy_n Algorithms

C++11 added the copy algorithms copy_if and copy_n. The copy_if algorithm copies each element from a range if the unary predicate function in its fourth argument returns true for that element. The iterators supplied as the first two arguments must be input iterators. The iterator supplied as the third argument must be an output iterator so that the element being copied can be written into to the copy location. This algorithm returns an iterator positioned after the last element copied.

The copy_n algorithm copies the number of elements specified by its second argument from the location specified by its first argument (an input iterator). The elements are output to the location specified by its third argument (an output iterator).

16.4.9 inplace_merge, unique_copy and reverse_copy

Figure 16.10 demonstrates algorithms inplace_merge, unique_copy and reverse_copy.

Fig. 16.10 Algorithms inplace_merge, reverse_copy and unique_copy.

Alternate View

 1   // Fig. 16.10: fig16_10.cpp
 2   // Algorithms inplace_merge, reverse_copy and unique_copy.
 3   #include <iostream>
 4   #include <algorithm> // algorithm definitions
 5   #include <array> // array class-template definition
 6   #include <vector> // vector class-template definition
 7   #include <iterator> // back_inserter definition
 8   using namespace std;
 9
10   int main() {
11      const int SIZE{10};
12      array<int, SIZE> a1{1, 3, 5, 7, 9, 1, 3, 5, 7, 9};
13      ostream_iterator<int> output(cout, " ");
14
15      cout << "array a1 contains: ";
16      copy(a1.cbegin(), a1.cend(), output);
17
18      // merge first half of a1 with second half of a1 such that
19      // a1 contains sorted set of elements after merge
20      inplace_merge(a1.begin(), a1.begin() + 5, a1.end());
21
22      cout << "
After inplace_merge, a1 contains: ";
23      copy(a1.cbegin(), a1.cend(), output);
24
25      vector<int> results1;
26
27      // copy only unique elements of a1 into results1
28      unique_copy(a1.cbegin(), a1.cend(), back_inserter(results1));
29      cout << "
After unique_copy results1 contains: ";
30      copy(results1.cbegin(), results1.cend(), output);
31
32      vector<int> results2;
33
34      // copy elements of a1 into results2 in reverse order
35      reverse_copy(a1.cbegin(), a1.cend(), back_inserter(results2));
36      cout << "
After reverse_copy, results2 contains: ";
37      copy(results2.cbegin(), results2.cend(), output);
38      cout << endl;
39   }

array a1 contains: 1 3 5 7 9 1 3 5 7 9
After inplace_merge, a1 contains: 1 1 3 3 5 5 7 7 9 9
After unique_copy results1 contains: 1 3 5 7 9
After reverse_copy, results2 contains: 9 9 7 7 5 5 3 3 1 1

inplace_merge Algorithm

Line 20 uses the inplace_merge algorithm to merge two sorted sequences of elements in the same container. In this example, the elements from a1.begin() up to, but not including, a1.begin() + 5 are merged with the elements from a1.begin() + 5 up to, but not including, a1.end(). This algorithm requires its three iterator arguments to be at least bidirectional iterators. A second version of this algorithm takes as a fourth argument a binary predicate function for comparing elements in the two sequences.

unique_copy Algorithm

Line 28 uses the unique_copy algorithm to make a copy of all the unique elements in the sorted sequence of values from a1.cbegin() up to, but not including, a1.cend(). The copied elements are placed into vector results1. The first two arguments must be at least input iterators and the last must be at least an output iterator. In this example, we did not preallocate enough elements in results1 to store all the elements copied from a1. Instead, we use function back_inserter (defined in header <iterator>), as discussed in Section 16.4.8, to insert elements at the end of results1. A second version of the unique_copy algorithm takes as a fourth argument a binary predicate function for comparing elements for equality.

reverse_copy Algorithm

Line 35 uses the reverse_copy algorithm to make a reversed copy of the elements in the range from a1.cbegin() up to, but not including, a1.cend(). The copied elements are inserted into results2 using a back_inserter object to ensure that the vector can grow to accommodate the appropriate number of elements copied. Algorithm reverse_copy requires its first two iterator arguments to be at least bidirectional iterators and its third to be at least an output iterator.

16.4.10 Set Operations

Figure 16.11 demonstrates algorithms includes, set_difference, set_intersection, set_symmetric_difference and set_union for manipulating sets of sorted values.

Fig. 16.11 Algorithms includes, set_difference, set_intersection, set_symmetric_difference and set_union.

Alternate View

 1   // Fig. 16.11: fig16_11.cpp
 2   // Algorithms includes, set_difference, set_intersection,
 3   // set_symmetric_difference and set_union.
 4   #include <iostream>
 5   #include <array>
 6   #include <algorithm> // algorithm definitions
 7   #include <iterator> // ostream_iterator
 8   using namespace std;
 9
10   int main() {
11      const size_t SIZE1{10}, SIZE2{5}, SIZE3{20};
12      array<int, SIZE1> a1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
13      array<int, SIZE2> a2{4, 5, 6, 7, 8};
14      array<int, SIZE2> a3{4, 5, 6, 11, 15};
15      ostream_iterator<int> output{cout, " "};
16
17      cout << "a1 contains: ";
18      copy(a1.cbegin(), a1.cend(), output); // display array a1
19      cout << "
a2 contains: ";
20      copy(a2.cbegin(), a2.cend(), output); // display array a2
21      cout << "
a3 contains: ";
22      copy(a3.cbegin(), a3.cend(), output); // display array a3
23
24      // determine whether a2 is completely contained in a1
25      if (includes(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend())) {
26         cout << "

a1 includes a2";
27      }
28      else {
29         cout << "

a1 does not include a2";
30      }
31
32      // determine whether a3 is completely contained in a1
33      if (includes(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cend())) {
34         cout << "
a1 includes a3";
35      }
36      else {
37      cout << "
a1 does not include a3";
38      }
39
40      array<int, SIZE1> difference;
41
42     // determine elements of a1 not in a2
43     auto result1 = set_difference(a1.cbegin(), a1.cend(),
44        a2.cbegin(), a2.cend(), difference.begin());      
45     cout << "

set_difference of a1 and a2 is: ";
46     copy(difference.begin(), result1, output);
47
48      array<int, SIZE1> intersection;
49
50      // determine elements in both a1 and a2
51      auto result2 = set_intersection(a1.cbegin(), a1.cend(),
52         a2.cbegin(), a2.cend(), intersection.begin());      
53      cout << "

set_intersection of a1 and a2 is: ";
54      copy(intersection.begin(), result2, output);
55
56      array<int, SIZE1 + SIZE2> symmetric_difference;
57
58      // determine elements of a1 that are not in a3 and
59      // elements of a3 that are not in a1
60      auto result3 = set_symmetric_difference(a1.cbegin(), a1.cend(),
61         a3.cbegin(), a3.cend(), symmetric_difference.begin());      
62      cout << "

set_symmetric_difference of a1 and a3 is: ";
63      copy(symmetric_difference.begin(), result3, output);
64
65      array<int, SIZE3> unionSet;
66
67      // determine elements that are in either or both sets
68      auto result4 = set_union(a1.cbegin(), a1.cend(),
69         a3.cbegin(), a3.cend(), unionSet.begin());   
70      cout << "

set_union of a1 and a3 is: ";
71      copy(unionSet.begin(), result4, output);
72      cout << endl;
73   }

a1 contains: 1 2 3 4 5 6 7 8 9 10
a2 contains: 4 5 6 7 8
a3 contains: 4 5 6 11 15

a1 includes a2
a1 does not include a3

set_difference of a1 and a2 is: 1 2 3 9 10

set_intersection of a1 and a2 is: 4 5 6 7 8

set_symmetric_difference of a1 and a3 is: 1 2 3 7 8 9 10 11 15

set_union of a1 and a3 is: 1 2 3 4 5 6 7 8 9 10 11 15

includes Algorithm

Lines 25 and 33 call the includes algorithm, which compares two sets of sorted values to determine whether every element of the second set is in the first set. If so, includes returns true; otherwise, it returns false. The first two iterator arguments must be at least input iterators and must describe the first set of values. In line 25, the first set consists of the elements from a1.cbegin() up to, but not including, a1.cend(). The last two iterator arguments must be at least input iterators and must describe the second set of values. In line 25, the second set consists of the elements from a2.cbegin() up to, but not including, a2.cend(). An overloaded version of includes takes a fifth argument that’s a binary predicate function indicating whether its first argument is less than its second. The two sequences must be sorted using the same comparison function.

set_difference Algorithm

Lines 43–44 use the set_difference algorithm to find the elements from the first set of sorted values that are not in the second set of sorted values (both sets of values must be in ascending order). The elements that are different are copied into the fifth argument (in this case, the array difference). The first two iterator arguments must be at least input iterators for the first set of values. The next two iterator arguments must be at least input iterators for the second set of values. The fifth argument must be at least an output iterator indicating where to store a copy of the values that are different. The algorithm returns an output iterator positioned immediately after the last value copied into the set to which the fifth argument points. An overloaded version of set_difference takes a sixth argument that’s a binary predicate function indicating whether its first argument is less than its second. The two sequences must be sorted using the same comparison function.

set_intersection Algorithm

Lines 51–52 use the set_intersection algorithm to determine the elements from the first set of sorted values that are in the second set of sorted values (both sets of values must be in ascending order). The elements common to both sets are copied into the fifth argument (in this case, array intersection). The first two iterator arguments must be at least input iterators for the first set of values. The next two iterator arguments must be at least input iterators for the second set of values. The fifth argument must be at least an output iterator indicating where to store a copy of the values that are the same. The algorithm returns an output iterator positioned immediately after the last value copied into the set to which the fifth argument points. An overloaded version of set_intersection takes a sixth argument that’s a binary predicate function indicating whether its first argument is less than its second. The two sequences must be sorted using the same comparison function.

set_symmetric_difference Algorithm

Lines 60–61 use the set_symmetric_difference algorithm to determine the elements in the first set that are not in the second set and the elements in the second set that are not in the first set (both sets must be in ascending order). The elements that are different are copied from both sets into the fifth argument (the array symmetric_difference). The first two iterator arguments must be at least input iterators for the first set of values. The next two iterator arguments must be at least input iterators for the second set of values. The fifth argument must be at least an output iterator indicating where to store a copy of the values that are different. The algorithm returns an output iterator positioned immediately after the last value copied into the set to which the fifth argument points. An overloaded version of set_symmetric_difference takes a sixth argument that’s a binary predicate function indicating whether its first argument is less than its second. The two sequences must be sorted using the same comparison function.

set_union Algorithm

Lines 68–69 use the set_union algorithm to create a set of all the elements that are in either or both of the two sorted sets (both sets of values must be in ascending order). The elements are copied from both sets into the fifth argument (in this case the array unionSet). Elements that appear in both sets are copied only from the first set. The first two iterator arguments must be at least input iterators for the first set of values. The next two iterator arguments must be at least input iterators for the second set of values. The fifth argument must be at least an output iterator indicating where to store the copied elements. The algorithm returns an output iterator positioned immediately after the last value copied into the set to which the fifth argument points. An overloaded version of set_union takes a sixth argument that’s a binary predicate function indicating whether its first argument is less than its second. The two sequences must be sorted in ascending order using the same comparison function.

16.4.11 lower_bound, upper_bound and equal_range

Figure 16.12 demonstrates algorithms lower_bound, upper_bound and equal_range.

Fig. 16.12 Algorithms lower_bound, upper_bound and equal_range for a sorted sequence of values.

Alternate View

 1   // Fig. 16.12: fig16_12.cpp
 2   // Algorithms lower_bound, upper_bound and
 3   // equal_range for a sorted sequence of values.
 4   #include <iostream>
 5   #include <algorithm> // algorithm definitions
 6   #include <array> // aray class-template definition
 7   #include <iterator> // ostream_iterator
 8   using namespace std;
 9
10   int main() {
11      const size_t SIZE{10};
12      array<int, SIZE> a{2, 2, 4, 4, 4, 6, 6, 6, 6, 8};
13      ostream_iterator<int> output{cout, " "};
14
15      cout << "array a contains: ";
16      copy(a.cbegin(), a.cend(), output);
17
18      // determine lower-bound insertion point for 6 in a
19      auto lower = lower_bound(a.cbegin(), a.cend(), 6);
20      cout << "

Lower bound of 6 is element "
21         << (lower - a.cbegin()) << " of array a";
22
23      // determine upper-bound insertion point for 6 in a
24      auto upper = upper_bound(a.cbegin(), a.cend(), 6);
25      cout << "
Upper bound of 6 is element "
26         << (upper - a.cbegin()) << " of array a";
27
28      // use equal_range to determine both the lower- and
29      // upper-bound insertion points for 6
30      auto eq = equal_range(a.cbegin(), a.cend(), 6);
31      cout << "
Using equal_range:
 Lower bound of 6 is element "
32           << (eq.first - a.cbegin()) << " of array a";
33      cout << "
 Upper bound of 6 is element "
34         << (eq.second - a.cbegin()) << " of array a";
35      
36      // determine lower-bound insertion point for 5 in a
37      cout << "

Use lower_bound to locate the first point
"
38         << "at which 5 can be inserted in order";
39      lower = lower_bound(a.cbegin(), a.cend(), 5);
40      cout << "
 Lower bound of 5 is element "
41         << (lower - a.cbegin()) << " of array a";
42
43      // determine upper-bound insertion point for 7 in a
44      cout << "

Use upper_bound to locate the last point
"
45         << "at which 7 can be inserted in order";
46      upper = upper_bound(a.cbegin(), a.cend(), 7);
47      cout << "
 Upper bound of 7 is element "
48         << (upper - a.cbegin()) << " of array a";
49
50      // use equal_range to determine both the lower- and
51      // upper-bound insertion points for 5
52      cout << "

Use equal_range to locate the first and
"
53         << "last point at which 5 can be inserted in order";
54      eq = equal_range(a.cbegin(), a.cend(), 5);
55      cout << "
 Lower bound of 5 is element "
56         << (eq.first - a.cbegin()) << " of array a";
57      cout << "
 Upper bound of 5 is element "
58         << (eq.second - a.cbegin()) << " of array a" << endl;
59   }

Array a contains: 2 2 4 4 4 6 6 6 6 8

Lower bound of 6 is element 5 of array a
Upper bound of 6 is element 9 of array a
Using equal_range:
   Lower bound of 6 is element 5 of array a
   Upper bound of 6 is element 9 of array a

Use lower_bound to locate the first point
at which 5 can be inserted in order
   Lower bound of 5 is element 5 of array a

Use upper_bound to locate the last point
at which 7 can be inserted in order
   Upper bound of 7 is element 9 of array a

Use equal_range to locate the first and
last point at which 5 can be inserted in order
  Lower bound of 5 is element 5 of array a
  Upper bound of 5 is element 5 of array a

lower_bound Algorithm

Line 19 uses the lower_bound algorithm to find the first location in a sorted sequence of values at which the third argument could be inserted in the sequence such that the sequence would still be sorted in ascending order. The first two iterator arguments must be at least forward iterators. The third argument is the value for which to determine the lower bound. The algorithm returns a forward iterator pointing to the position at which the insert can occur. A second version of lower_bound takes as a fourth argument a binary predicate function indicating the order in which the elements were originally sorted.

upper_bound Algorithm

Line 24 uses the upper_bound algorithm to find the last location in a sorted sequence of values at which the third argument could be inserted in the sequence such that the sequence would still be sorted in ascending order. The first two iterator arguments must be at least forward iterators. The third argument is the value for which to determine the upper bound. The algorithm returns a forward iterator pointing to the position at which the insert can occur. A second version of upper_bound takes as a fourth argument a binary predicate function indicating the order in which the elements were originally sorted.

equal_range Algorithm

Line 30 uses the equal_range algorithm to return a pair of forward iterators containing the results of performing both a lower_bound and an upper_bound operation. The first two arguments must be at least forward iterators. The third is the value for which to locate the equal range. The algorithm returns a pair of forward iterators for the lower bound (eq.first) and upper bound (eq.second), respectively.

Locating Insertion Points in Sorted Sequences

Algorithms lower_bound, upper_bound and equal_range are often used to locate insertion points in sorted sequences. Line 39 uses lower_bound to locate the first point at which 5 can be inserted in order in a. Line 46 uses upper_bound to locate the last point at which 7 can be inserted in order in a. Line 54 uses equal_range to locate the first and last points at which 5 can be inserted in order in a.

16.4.12 min, max, minmax and minmax_element

Figure 16.13 demonstrates algorithms min, max, minmax and minmax_element.

Fig. 16.13 Algorithms min, max, minmax and minmax_element.

Alternate View

 1   // Fig. 16.13: fig16_13.cpp
 2   // Algorithms min, max, minmax and minmax_element.
 3   #include <iostream>
 4   #include <array>
 5   #include <algorithm>
 6   using namespace std;
 7
 8   int main() {
 9      cout << "The minimum of 12 and 7 is: " << min(12, 7);
10      cout << "
The maximum of 12 and 7 is: " << max(12 ,7);
11      cout << "
The minimum of 'G' and 'Z' is: " << min('G', 'Z');
12      cout << "
The maximum of 'G' and 'Z' is: " << max('G' ,'Z');
13
14      // determine which argument is the min and which is the max
15      auto result1 = minmax(12, 7);
16      cout << "

The minimum of 12 and 7 is: " << result1.first
17         << "
The maximum of 12 and 7 is: " << result1.second;
18
19      array<int, 10> items{3, 100, 52, 77, 22, 31, 1, 98, 13, 40};
20      ostream_iterator<int> output{cout, " "};
21
22      cout << "

Array items contains: ";
23      copy(items.cbegin(), items.cend(), output);
24
25      auto result2 = minmax_element(items.cbegin(), items.cend());
26      cout << "
The minimum element in items is: " << *result2.first
27         << "
The maximum element in items is: " << *result2.second << endl;
28   }

The minimum of 12 and 7 is: 7
The maximum of 12 and 7 is: 12
The minimum of 'G' and 'Z' is: G
The maximum of 'G' and 'Z' is: Z

The minimum of 12 and 7 is: 7
The maximum of 12 and 7 is: 12

Array items contains: 3 100 52 77 22 31 1 98 13 40
The minimum element in items is: 1
The maximum element in items is: 100

Algorithms min and max with Two Parameters

Algorithms min and max (demonstrated in lines 9–12) determine the minimum and the maximum of two elements, respectively.

C++11: min and max Algorithms with initializer_list Parameters

C++11 added overloaded versions of the algorithms min and max that each receive an initializer_list parameter and return the smallest or largest item in the list initializer that’s passed as an argument. For example, the following statement returns 7:


int minimum = min({ 10, 7, 14, 21, 17 });

Each of these new min and max algorithms is overloaded with a version that takes as a second argument a binary predicate function for determining whether the first argument is less than the second.

C++11: minmax Algorithm

C++11 also added the minmax algorithm (line 15) that receives two items and returns a pair in which the smaller item is stored in first and the larger item is stored in second. A second version of this algorithm takes as a third argument a binary predicate function for determining whether the first argument is less than the second.

C++11: minmax_element Algorithm

In addition, C++11 added the minmax_element algorithm (line 25) that receives two input iterators representing a range of elements and returns a pair of iterators in which first points to the smallest element in the range and second points to the largest. A second version of this algorithm takes as a third argument a binary predicate function for determining whether the first argument is less than the second.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset