
Real Power of STL Revealed  Part III Binders and Function Adapters As have been said before, some STL algorithms require unary function objects (with one argument), other binary function objects (with two arguments). One example is STL transform. First version takes one input iterator range and assigns result returned by unary function object to corresponding output iterator; second works similar way but uses two input iterator ranges and binary function object. Obviously, function object's argument(s) is assigned a value pointed by the input iterator(s). Let's declare two containers with 5 items and fill them with "6" and random values respectively. vector<long> v1(5, 6); Here is the result: vector with initial values: 6 6 6 6 6 Now let's take both containers, add corresponding items to each other, and put result back into the vector. Note: we will not print content of deque because it will remain the same. transform(v1.begin(), v1.end(), dq.begin(), v1.begin(), plus<long>()); STL transform()
sample with two input iterator ranges and binary predicate 'plus': How to use transform() with unary predicate is shown below. transform(v1.begin(), v1.end(), v1.begin(), negate<long>()); STL transform()
sample with one input iterator range and unary predicate 'negate': Now suppose we need to add "7" to each item of vector with transform() which takes only one input iterator range (obviously it is just stupid to create second vector filled with "7" to do this simple task). Unfortunately, STL plus is a binary predicate and cannot be used direct way with this version of transform. However, we can use so called binder to bind certain value to second argument of binary predicate. long valueToBind = 7; STL transform()
sample with one input iterator range and binary predicate 'plus'
with binder: By the way, we can use bind1st() to bind desired value to the first argument of binary predicate. Resume: binders are necessary to allow binary function objects to be used with algorithms which require unary function objects and vice versa. Function adapter ptr_fun takes a pointer to unary (or binary) function and turns it into unary (or binary) function object. Let's assume we have vector<char *> which needs to be modified and sorted with STL algorithms. It is impossible to use for example STL sort() directly because pointers to strings will be sorted instead of strings themselves. So, first, we need extra auxiliary functions on the basis of standard C routine strcmp(). This is because strcmp() returns negative, zero, or positive integer, not bool, what is required for us. Inline bool my_strcmp_equal(const char *c1,
const char *c2) inline bool my_strcmp_less(const char *c1,
const char *c2) Now we are ready to start. char c1[] = "Andrew", c2[] = "Serge
", c3[] = "Joanne"; Result: Suppose we need to replace "Andrew" with "Andre" by French manner. replace_if(vc.begin(), vc.end(), bind2nd(ptr_fun(my_strcmp_equal), c1), "Andre "); STL replace_if()
with function adapter 'ptr_fun'
and binder test: STL sort() can be used the same way to sort items in ascending and descending order. random_shuffle(vc.begin(), vc.end()); sort(vc.begin(), vc.end(), ptr_fun(my_strcmp_less)); STL sort()
with function adapter 'ptr_fun'
test: sort(vc.begin(), vc.end(), not2(ptr_fun(my_strcmp_less))); STL sort()
with function adapter 'ptr_fun'
and negator 'not2' test: Pay attention to the so called negator "not2". Negators "not1" and "not2" return (! func_obj()) of unary and binary function objects respectively. You can view or download source code, as well as output in text format. Q. Why we need unary/binary predicates and function
adaptors when we have operators like "+",
"", "*"
already defined? Q. Does usage of function objects cause slowdown of
generated codes? Q. I would like to take a look at sources of standard
STL predicates in order to understand what and how is going on. template <class
T> T operator()(const T& x, const T& y) const { return x + y; };}; template <class
T> T operator()(const T& x) const { return x; };}; template <class
T> bool operator()(const T& x, const T& y) const { return x == y; }; }; Memory Allocation Any library could be hardly to called portable if it depends on certain memory model. For example, DOS applications may use various memory models. For MacOS with uniform memory model it is just not an issue. With STL, developers can incorporate its own Allocator class which abstracts library from all lowlevel memory related details. By default, STL provides its own default allocator class which is used if no other Allocator classes have been specified. You can refer to STL reference in order to learn how to write your own Allocator class. However, this is not needed in most cases. Quick STL container overview
PS. Now, after review of even very small fraction of STL features, you can understand, how powerful STL is. Just consider, how much time and effort from your side might be required if you have to design all above algorithms from scratch. Continued  Useful STL Sample: Dynamic Array of Generic Strings... 