Examples
Team LiB
Previous Section Next Section

Examples

Here are two examples adapted from [Meyers01]:

Example 1: Transforming a deque. After working through several incorrect iterations that ran afoul of iterator invalidation issues (e.g., see Item 83), we finally come up with the following correct handwritten loop for adding 41 to every element of data, an array of doubles, and placing the result at the beginning of d, a deque<double>:



deque<double>::iterator current = d.begin();





for( size_t i = 0; i < max; ++i ) {


  current = d.insert( current, data[i] + 41 );    // be careful to keep current valid…


  ++current;                                      // … then increment it when it's safe


}



An algorithm call would have bypassed the correctness pitfalls straight away:



transform( data, data + max,               // copy elements from data


           inserter(d, d.begin()),              // to the front of d


           bind2nd(plus<double>(), 41) ); // adding 41 to each



Granted, bind2nd and plus are awkward. Frankly, nobody really uses them much, and that's just as well because they hurt readability (see 6).

But lambda functions, which generate the function object for us, let us write simply:



transform( data, data + max, inserter( d, d.begin() ), _1 + 41 );



Example 2: Find the first element between x and y. Consider this naked loop that searches a vector<int> v for the first value between x and y, by calculating an iterator that points to the found element or to v.end():



for( vector<int>::iterator i = v.begin(); i != v.end(); ++i )


 if( *i > x && *i < y ) break;



An algorithm call is problematic. Absent lambdas, the two options are to write a custom function object or to use the standard binders. Alas, with the binders option we can't use the standard binders alone but need to use the nonstandard (although widely-available) compose2 adapter, and even at that the code is just impenetrable, and nobody would ever really write it:



vector<int>::iterator i = find_if( v.begin(), v.end(),


                                   compose2( logical_and<bool>(),


                                            bind2nd(greater<int>(), x),


                                            bind2nd(less<int>(), y) ) );



The other option, namely writing a custom function object, is viable. It looks good at the call point, and its main drawback is that it requires writing a BetweenValues function object that moves the body's logic visually away from the call point:



template<typename T>


class BetweenValues : public unary_function<T, bool> {


public:


  BetweenValues( const T& low, const T& high ) : low_(low), high_(high) {}


  bool operator()( const T& val ) const {return val > low_ && val < high_; }





private:                                        // far away from the point of use


  T low_, high_;


};





vector<int>::iterator i = find_if( v.begin(), v.end(), BetweenValues<int>(x, y) );



Lambda functions, which generate the function object for us, let us write simply:



vector<int>::iterator i = find_if( v.begin(), v.end(), _1 > x && _1 < y );



    Team LiB
    Previous Section Next Section