Выбрать главу

void copy(T* begin, T* end, T* dest) {

  while (begin != end)

    *dest++ = *begin++;

}.

We say "almost," because copy( ) can actually process sequences delimited by anything that acts like a pointer, such as an iterator. In this way, copy( ) can duplicate a vector, as in the following example.

//: C06:CopyVector.cpp

// Copies the contents of a vector

#include <algorithm>

#include <cassert>

#include <cstddef>

#include <vector>

using namespace std;

int main() {

  int a[] = {10, 20, 30};

  const size_t SIZE = sizeof a / sizeof a[0];

  vector<int> v1(a, a + SIZE);

  vector<int> v2(SIZE);

  copy(v1.begin(), v1.end(), v2.begin());

  assert(equal(v1.begin(), v1.end(), v2.begin()));

} ///:~.

The first vector, v1, is initialized from the sequence of integers in the array a. The definition of the vector v2 uses a different vector constructor that makes room for SIZE elements, initialized to zero (the default value for integers).

As with the array example earlier, it’s important that v2 have enough space to receive a copy of the contents of v1. For convenience, a special library function, back_inserter( ), returns a special type of iterator that inserts elements instead of overwriting them, so memory is expanded automatically by the container as needed. The following example uses back_inserter( ), so it doesn’t have to expand the size of the output vector, v2, ahead of time.

//: C06:InsertVector.cpp

// Appends the contents of a vector to another

#include <algorithm>

#include <cassert>

#include <cstddef>

#include <iterator>

#include <vector>

using namespace std;

int main() {

  int a[] = {10, 20, 30};

  const size_t SIZE = sizeof a / sizeof a[0];

  vector<int> v1(a, a + SIZE);

  vector<int> v2;  // v2 is empty here

  copy(v1.begin(), v1.end(), back_inserter(v2));

  assert(equal(v1.begin(), v1.end(), v2.begin()));

} ///:~

The back_inserter( ) function is defined in the <iterator> header. We’ll explain how insert iterators work in depth in the next chapter.

Since iterators are identical to pointers in all essential ways, you can write the algorithms in the standard library in such a way as to allow both pointer and iterator arguments. For this reason, the implementation of copy( ) looks more like the following code.

template<typename Iterator>

void copy(Iterator begin, Iterator end, Iterator dest) {

  while (begin != end)

    *begin++ = *dest++;

}

Whichever argument type you use in the call, copy( ) assumes it properly implements the indirection and increment operators. If it doesn’t, you’ll get a compile-time error.

Predicates

At times, you might want to copy only a well-defined subset of one sequence to another, such as only those elements that satisfy a certain condition. To achieve this flexibility, many algorithms have alternate calling sequences that allow you to supply a predicate, which is simply a function that returns a Boolean value based on some criterion. Suppose, for example, that you only want to extract from a sequence of integers those numbers that are less than or equal to 15. A version of copy( ) called remove_copy_if( ) can do the job, like this:.

//: C06:CopyInts2.cpp

// Ignores ints that satisfy a predicate

#include <algorithm>

#include <cstddef>

#include <iostream>

using namespace std;

// You supply this predicate

bool gt15(int x) {

  return 15 < x;

}

int main() {

  int a[] = {10, 20, 30};

  const size_t SIZE = sizeof a / sizeof a[0];

  int b[SIZE];

  int* endb = remove_copy_if(a, a+SIZE, b, gt15);

  int* beginb = b;

  while (beginb != endb)

    cout << *beginb++ << endl; // Prints 10 only

} ///:~.

The remove_copy_if( ) function template takes the usual range-delimiting pointers, followed by a predicate of your choosing. The predicate must be a pointer to function[79] that takes a single argument of the same type as the elements in the sequence, and it must return a bool. In this case, the function gt15 returns true if its argument is greater than 15. The remove_copy_if( ) algorithm applies gt15( ) to each element in the input sequence and ignores those elements when writing to the output sequence.

The following program illustrates yet another variation of the copy algorithm.

//: C06:CopyStrings2.cpp

// Replaces strings that satisfy a predicate

#include <algorithm>

#include <cstddef>

#include <iostream>

#include <string>

using namespace std;

// The predicate

bool contains_e(const string& s) {

  return s.find('e') != string::npos;

}

int main() {

  string a[] = {"read", "my", "lips"};

  const size_t SIZE = sizeof a / sizeof a[0];

  string b[SIZE];

  string* endb =

    replace_copy_if(a, a + SIZE, b, contains_e,

     string("kiss"));

  string* beginb = b;

  while (beginb != endb)

    cout << *beginb++ << endl;

} ///:~.

Instead of just ignoring elements that don’t satisfy the predicate, replace_copy_if( ) substitutes a fixed value for such elements when populating the output sequence. The output in this case is

kiss

my

lips

because the original occurrence of "read", the only input string containing the letter e, is replaced by the word "kiss", as specified in the last argument in the call to replace_copy_if( ).

The replace_if( ) algorithm changes the original sequence in place, instead of writing to a separate output sequence, as the following program shows.

//: C06:ReplaceStrings.cpp

// Replaces strings in-place

#include <algorithm>

#include <cstddef>

#include <iostream>

#include <string>

using namespace std;

bool contains_e(const string& s) {

  return s.find('e') != string::npos;

}

int main() {

  string a[] = {"read", "my", "lips"};

  const size_t SIZE = sizeof a / sizeof a[0];

  replace_if(a, a + SIZE, contains_e, string("kiss"));