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 ]