The first form of transform( ) simply calls f(*first), where first ranges through the input sequence. Similarly, the second form calls f(*first1, *first2). (Note that the length of the second input range is determined by the length of the first.) The return value in both cases is the past-the-end iterator for the resulting output range.
Examples
Since much of what you do with objects in a container is to apply an operation to all those objects, these are fairly important algorithms and merit several illustrations.
First, consider for_each( ). This sweeps through the range, pulling out each element and passing it as an argument as it calls whatever function object it’s been given. Thus, for_each( ) performs operations that you might normally write out by hand. If you look in your compiler’s header file at the template defining for_each( ), you’ll see something like this:.
template
Function for_each(InputIterator first,
InputIterator last,
Function f) {
while (first != last) f(*first++);
return f;
}
The following example shows several ways this template can be expanded. First, we need a class that keeps track of its objects so we can know that it’s being properly destroyed:.
//: C06:Counted.h
// An object that keeps track of itself
#ifndef COUNTED_H
#define COUNTED_H
#include
#include
class Counted {
static int count;
char* ident;
public:
Counted(char* id) : ident(id) { count++; }
~Counted() {
std::cout << ident << " count = "
<< --count << std::endl;
}
};
int Counted::count = 0;
class CountedVector :
public std::vector
public:
CountedVector(char* id) {
for(int i = 0; i < 5; i++)
push_back(new Counted(id));
}
};
#endif // COUNTED_H ///:~
The class Counted keeps a static count of how many Counted objects have been created and tells you as they are destroyed.[89] In addition, each Counted keeps a char* identifier to make tracking the output easier.
The CountedVector is derived from vector
//: C06:ForEach.cpp
// Use of STL for_each() algorithm
#include
#include
#include "Counted.h"
using namespace std;
// Function object:
template
class DeleteT {
public:
void operator()(T* x) { delete x; }
};
// Template function:
template
void wipe(T* x) { delete x; }
int main() {
CountedVector B("two");
for_each(B.begin(),B.end(),DeleteT
CountedVector C("three");
for_each(C.begin(), C.end(), wipe
} ///:~
Since this is obviously something you might want to do a lot, why not create an algorithm to delete all the pointers in a container? You could use transform( ). The value of transform( ) over for_each( ) is that transform( ) assigns the result of calling the function object into a resulting range, which can actually be the input range. That case means a literal transformation for the input range, since each element would be a modification of its previous value. In this example, this approach would be especially useful since it’s more appropriate to assign to each pointer the safe value of zero after calling delete for that pointer. Transform( ) can easily do this:.
//: C06:Transform.cpp
// Use of STL transform() algorithm
#include "Counted.h"
#include
#include
#include
using namespace std;
template
T* deleteP(T* x) { delete x; return 0; }
#ifdef _MSC_VER
// Microsoft needs explicit instantiation
template Counted* deleteP(Counted* x);
#endif
template
T* operator()(T* x) { delete x; return 0; }
};
int main() {
CountedVector cv("one");
transform(cv.begin(), cv.end(), cv.begin(),
deleteP
CountedVector cv2("two");
transform(cv2.begin(), cv2.end(), cv2.begin(),
Deleter
} ///:~
This shows both approaches: using a template function or a templatized function object. After the call to transform( ), the vector contains five null pointers, which is safer since any duplicate deletes will have no effect.
One thing you cannot do is delete every pointer in a collection without wrapping the call to delete inside a function or an object. That is, you do the following:.
for_each(a.begin(), a.end(), ptr_fun(operator delete));
This has the same problem as the call to destroy ( ) did earlier: operator delete( ) takes a void*, but iterators aren’t void pointers (or pointers at all). Even if you could make it compile, what you’d get is a sequence of calls to the function that releases the storage. You will not get the effect of calling delete for each pointer in a, however; the destructor will not be called. This is typically not what you want, so you will need wrap your calls to delete.