The function binders that come with the C++ Standard Library are that library's attempt at the construct known variously as "lambda", "currying function", or "closure".   Being an example of generic programming, the function binders are, of course, templates, whose arguments must meet specific requirements before you can use them.  Each binder consists of a class definition which contains the bound operator, and a function which does the binding.

Thus:

namespace std {

template <class Operation>
class binder1st: public unary_function<typename Operation::second_argument_type,
                                       typename Operation::result_type>
{
 protected:
 Operation Op;
 typename Operation::first_argument_type value;
 public:
 binder1st (const Operation &x,
            const typename Operation::first_argument_type y): Op (x), value (y) {}
 typename Operation::result_type operator()(const typename Operation::second_argument_type &X) const
 {
  return Op (value, X);
 }
};
 

template <class Operation, class T>
binder1st<Operation> bind1st (const Operaiton &Op, const T &x)
{
 return binder1st<Operation (Op, x);
}

} // namespace std
 

A Binder2nd class and a bind2nd() function are defined analogously.

To use this beast, you need a functor class which meets the requirements:

A type declaration specifying the functor's first argument type,
A type declaration specifying the functor's second argument type,
A type declaration specifying the functor's result type, and
Finally, a declared Operator() that performs the actual operaiton.

Have I scared you away yet?  Just wait!

Why would you use a function binder in the first place?   Well, perhaps you want to use an algorithm from the Standard Library that operates on one argument, but the operation you wanted to use takes two arguments, with one always the same.  Let's say you had a std::vector<int> and you wanted to count the ints less than 37.   You can use std::count_if and std::bind2nd to do this for you:

//
// In some header somewhere
//

template <class N1, class N2>
struct Less
{
 typedef N1 first_argument_type;
 typedef N2 second_argument_type;
 typedef bool result_type;
 static bool operator()(N1 const &n1, N2 const &n2)
 {
  return n1<n2;
 }
};

//
// Later, Down in your code, count the number of
// elements of a vector less than 37.
//

int lt37 (std::vector<int> const &vi)
{
 return std::count_if (vi.begin(), vi.end(), std::bind2nd(Less<int,int>(), 37));
}

Ugly, huh? The ungainly syntax required to use the function binders drives a lot of people away from using them (much like a lot of the rest of the C++ Standard Library).

On top of all that, these definitions are rather limiting.

  • You can't use them with functions that have a void return type.
  • You need to declare an Operation object even if it doesn't have any state (the temporary object Less<int,int>() above shouldn't be necessary).
  • You can only bind to functions that don't change their arguments!
This last problem is the one that leaves people fuming at the Standards Committee.

So, if you had:

template <class OSTREAM, class T>
struct Inserter
{
 typedef OSTREAM &first_argument_type;
 typedef T second_argument_type;
 typedef OSTREAM result_type;
 static OSTREAM &operator()(OSTREAM &os, T const &t)
 {
  return os::operator<<(t);
 }
};

You couldn't use bind1st() to bind to a particular ostream into an Inserter object for use in a standard algorithm.

std::ostream &dumpvec (std::ostream &os, std::vector<int> const &v)
{
 //
 // Sorry, won't work!
 // (can't take a reference to a reference)
 //
 return std::for_each (v.begin(),
                       v.end(),
                       std::bind1st (Inserter<std::ostream,
                                              int         >, os));
}

The problem arises from the over-zealous use of const references for function arguments.
Bjarne Stroustrup himself has submitted a defect report for the last problem, plus a possible solution1, but the Committee has yet to agree upon whether it's even a defect in the Standard or not.

In the meantime, there are two solutions.

  • Roll your own binder1st and binder2nd, outside of namespace std. If you've read this far in the writeup, I suppose you know what you have to do to fix the problem.
  • I have read in a few places that you are allowed to specialize templates in the Standard Library for classes you define yourself.  Thus, you can make a specialization of std::binder1st<Operation> for any Operation you define!  Be warned: I'm not completely sure if you can do this.  The standard seems to be pretty strict on putting things in namespace std.
Thus:

namespace my {

template <class Operation>
class binder1st: public unary_function<typename Operation::second_argument_type,
                                       typename Operation::result_type>
{
  protected:
  Operation Op;
  typedef typename Operation::first_argument_type bound_type;
  bound_type value;
  public:
  typedef typename Operator::second_argument_type first_argument_type;
  typedef typename Operator::result_type result_type;
  binder1st (Operation x, bound_type y): Op (x), value (y) {}
  typename Operation::result_type operator()(typename Operation::second_argument_type X)
  {
   return Op (value, X);
  }
};

} // namespace my

Or, if allowed:

namespace std {

template <class T>
class binder1st<Inserter <std::ostream, T>/**/>: public unary_function<T, ostream>
{
 protected:
 Inserter<std::ostream, T> &os;
 typename Operation::first_argument_type value;
 public:
 binder1st (Operation const &x,
            typename Operation::first_argument_type y): Op (x), value (y) {}
 typename Operation::result_type operator()(const T &X)
 {
  return Op (value, X);
 }
};



1I disagree with the solution, but then again the Committee never heard of me.

Log in or register to write something here or to contact authors.