Topic: std::binder2nd (compilable)


Author: sbnaran@localhost.localdomain.COM (Siemel Naran)
Date: 1998/12/13
Raw View


/*
   We discuss the problem with
      class binder2nd<Oper>
   Specifically, the problem with its constructor
 */


// First, here is an ordinary class

struct X
{
     private:
          int x;

     public:
          explicit X(int x_=int()) : x(x_) { }
          int asInt() const { return x; }
          X plus(const X& that) const { return X(this->x+that.x); }
          X minus(const X& that) const { return X(this->x+that.x); }
};

/*
   We will now make an array of X objects: X x[3];
   Then we transform the array as follows:
      We step throuh each element in the array
      For each element, we evaluate the x[i].plus(X(4))
      We store the result in x[i]

   Using for loops, here's what we're doing
      X add(4);
      for (int i=0; i<3; i++) x[i]=x[i].plus(add);

   Now the same thing using the functional objects
      std::transform
        (x,x+3,x,
         std::bind2nd
            (std::const_mem_fun_ref(&X::plus),
             X(4)
            )
        );

 */



// Incidentally, you're not allowed to forward declare anything in
// namespace std, but let's ignore that rule for now.  Here is
// the relevant part of <functional> and <algorithm>, for
// convenience.

// Play close attention the constructor of template class binder2nd<Oper>
// In particular, note its second argument
// It's second argument is "const typename Oper::second_argument_type&"


namespace std // functional
{

template <class Arg, class Result>
struct unary_function
{
     typedef Arg    argument_type;
     typedef Result result_type  ;
};

template <class Arg1, class Arg2, class Result>
struct binary_function
{
     typedef Arg1   first_argument_type ;
     typedef Arg2   second_argument_type;
     typedef Result result_type         ;
};


template <class Result, class Object, class Arg>
class const_mem_fun1_ref_t : public binary_function<Object,Arg,Result>
{
     public:
          explicit const_mem_fun1_ref_t(Result (Object::*func_)(Arg) const)
               : func(func_)
          {
          }

          Result operator()(Object& object, Arg arg) const
          {
               return (object.*func)(arg);
          }

     private:
          Result (Object::*const func)(Arg) const;
};

template <class Result, class Object, class Arg> inline
const_mem_fun1_ref_t<Result,Object,Arg>
const_mem_fun_ref(Result (Object::*func)(Arg) const)
{
     return const_mem_fun1_ref_t<Result,Object,Arg>(func);
}


template <class Oper>
class binder2nd
     : public unary_function<typename Oper::first_argument_type,
                             typename Oper::result_type
                            >
{
     public:
          binder2nd(const Oper& oper_,
                    const typename Oper::second_argument_type& arg2_)
               : oper(oper_), arg2(arg2_)
          {
          }

          typename Oper::result_type
          operator()(typename Oper::first_argument_type& arg1)
          {
               return oper(arg1,arg2);
          }

     protected:
          Oper oper;
          typename Oper::second_argument_type arg2;
};

template <class Oper, class Arg2> inline
binder2nd<Oper>
bind2nd(const Oper& oper, const Arg2& arg2)
{
     return binder2nd<Oper>(oper,arg2);
}


} // namespace std


namespace std // algorithm
{

template <class ReadIter, class WriteIter, class UnaryFunction> inline
WriteIter transform
     (ReadIter source, const ReadIter source_end,
      WriteIter target,
      UnaryFunction function)
{
     while (source!=source_end) *target++=function(*source++);
     return target;
}

} // namespace std






// Now here's the body of main
// We use the std::transform method to transform the array
// BTW, for_each is a good choice too
// In the comments below, we show the results of
// template argument deduction.

#include <iostream.h>

int main()
{
     X x[3];

     for (int i=0; i<3; i++) cout << x[i].asInt() << ' '; cout << '\n';
     // should print "0 0 0"

     std::transform
        (x,x+3,x, // ReadIter==X*, WriteIter==X*
         std::bind2nd
            (std::const_mem_fun_ref(&X::plus),
                       // typeof(&X::plus) == X (Object::*)(const X&) const
                       // Object==X,
                       // Arg==const X&,
                       // Result==X
             X(4) // constructor binder2nd is expecting "const const X&&"
            )
        );

     for (int i=0; i<3; i++) cout << x[i].asInt() << ' '; cout << '\n';
     // should print "4 4 4"
}


/*
   Here's the essence of the problem:
   The argument of X::plus is "const X&"
   So the constructor of binder2nd expects a "const const X& &"
   This gives one warning and one error on my compiler:
      Warning: two consts in a row are meaningless
      Error  : can't have a reference to a reference

   But if we change the signature of X::plus to "X X::plus(X)" then
   The argument of X::plus is "X"
   So the constructor of binder2nd expects a "const X&"
   This compiles and runs fine.


   There are two solutions to the problem.

   First, constructor of binder2nd from
      binder2nd(const Oper&, const typename Oper::second_argument_type&);
   to
      binder2nd(const Oper&, typename Oper::second_argument_type);

   Second, make the rule the two consts in a row should not generate a
   warning if the consts arise in implicit template instantiation.
   And, make the rule that two references in a row is the same as one
   reference -- ie, "in&&" is the same as "in&".

   So, what goes?

 */

/*--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------/*


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]