Topic: Argument types for std::binary_function


Author: smeyers@aristeia.com (Scott Meyers)
Date: 2000/06/08
Raw View
On Fri, 2 Jun 2000 20:59:20 CST, Marc Girod wrote:
> >>>>>> "CR" == Chris Riesbeck <riesbeck@ils.nwu.edu> writes:
...
> Er... why wasn't this an answer?
>
> CR> to binary_function<>, the types you get
>
> CR> to the functor, the most restrictive types you can

Well, I suppose one can argue that it is "an answer," but the post went on
to say that the functor might take different arguments from those passed to
binary_function, which seems inconsistent with the advice to pass "the
types you get".  The post went further to remark that passing reference
types leads to problems, suggesting that if "the type you get" is a
reference, you may not want to propagate that to binary_function.  IMO, the
posting didn't really offer a firm suggestion on what types to pass to
binary_function.

BTW, the purpose of this posting is just to answer Marc's question, not to
knock Chris' reply to my original query.  I appreciate Chris taking the
time to offer an opinion on a question that, from what I can tell, doesn't
appear to have a particularly good answer, especially if you're writing a
template and "the type you get" is T, which may or may not be a reference
type.

Scott

---
[ 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              ]






Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: 2000/06/09
Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message
news:MPG.139de263717ade989896f5@news.supernews.com...
> This may seem like a silly question, but if I inherit from
> std::binary_function<Arg1Type, Arg2Type, ReturnType>, what types
should I
> pass?

I think you got an idea by now, but please give me a stab to it, too.
You know, answering Scott Meyers' posts looks good on one's resum   .

The argument types for std::*_function sport the same features as all
template-based interfaces: they are somewhat loosely-typed and
describe possible call syntax, rather than strict typifying.

I think the argument types for std::*_function mean that you can call
the functor passing it two values of those types, requirement that
const references fulfill.

But now let's get back to the fascinating thread on one-based
indexing.


Andrei






---
[ 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              ]






Author: "Mark Rodgers" <mark.rodgers@cadenza.co.nz>
Date: 2000/06/05
Raw View
<sirwillard@my-deja.com> wrote in message
news:8h5rq2$voo$1@nnrp1.deja.com...
> You summarized everything here very nicely.  You should know that
> there's an implementation of the standard functor classes included in
> the Boost library (http://www.boost.org). ...

I should point out that the alternative function object adapters you
mention are not yet in the Boost collection.  I've submitted them for
formal review and _hope_ that they will be accepted by Boost in the
not too distant future.  We'll see how it goes.

In the meantime, Boost members (see www.boost.org for details), can
get hold of them from "the vault", try them out, and offer feedback.
Please take this as an invitation to join Boost.  I have not been
involved with Boost for very long, but have rapidly come to the
conclusion that participation in their activities is a very rewarding
pastime.

Mark





---
[ 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              ]






Author: smeyers@aristeia.com (Scott Meyers)
Date: 2000/06/01
Raw View
This may seem like a silly question, but if I inherit from
std::binary_function<Arg1Type, Arg2Type, ReturnType>, what types should I
pass?  On the face of it, it seems like Arg1Type and Arg2Type should be the
same as the types of the functor's formal parameter types, but that's not
what the standard does.  Consider (from 23.3.2, pg. 494):

  class value_compare
  : public binary_function<value_type,value_type,bool> {
  friend class multimap;
  protected:
    Compare comp;
    value_compare(Compare c) : comp(c) {}
  public:
    bool operator()(const value_type& x, const value_type& y) const {
      return comp(x.first, y.first);
    }
  };

Here, the formal parameter types to value_compare::operator() are
of type

  const value_type&

but the types passed to binary_function are of type

  value_type

In general, type "T" is not identical to type "const T&", so why isn't
"const value_type&" passed to binary_function in the above example?

Again, then, I ask: if I inherit from std::binary_function<Arg1Type,
Arg2Type, ReturnType> (or std::unary_function), what types should I pass?

Thanks,

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ 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              ]






Author: Chris Riesbeck <riesbeck@ils.nwu.edu>
Date: 2000/06/01
Raw View
In article <MPG.139de263717ade989896f5@news.supernews.com>,
smeyers@aristeia.com (Scott Meyers) wrote:

>This may seem like a silly question,

to which no doubt I'm about to give a silly answer...

> but if I inherit from
>std::binary_function<Arg1Type, Arg2Type, ReturnType>, what types should I
>pass?

to binary_function<>, the types you get

to the functor, the most restrictive types you can

> On the face of it, it seems like Arg1Type and Arg2Type should be the
>same as the types of the functor's formal parameter types,

the functor's parameter types can be more restrictive, if the
functor's code permits

> Consider (from 23.3.2, pg. 494):
>
>  class value_compare
>  : public binary_function<value_type,value_type,bool> {
>  friend class multimap;
>  protected:
>    Compare comp;
>    value_compare(Compare c) : comp(c) {}
>  public:
>    bool operator()(const value_type& x, const value_type& y) const {
>      return comp(x.first, y.first);
>    }
>  };

since comp() should not modify its parameters, and doesn't need
a local copy, the coder restricts the functor's parameter
types to const &.

if value_type coming in is already a const & then, as I
currently understand the newsgroup discussions I've seen here:

  - the double const is treated as just const, by special dispensation

  - the double & is a problem, because similar dispensation
    for & doesn't exist

How silly was I?

---
[ 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              ]






Author: vaps4bm@prism.gatech.edu (Brian McNamara!)
Date: 2000/06/01
Raw View
smeyers@aristeia.com (Scott Meyers) once said:
>In general, type "T" is not identical to type "const T&", so why isn't
>"const value_type&" passed to binary_function in the above example?

>Again, then, I ask: if I inherit from std::binary_function<Arg1Type,
>Arg2Type, ReturnType> (or std::unary_function), what types should I pass?

I don't think I know "the answer" to the question, but this is one I've
thought about before, and I can at least tell you some of the issues
I've considered.

Consider bind2nd (code from gcc's implementation):

   template <class Operation>
   class binder2nd
     : public unary_function<typename Operation::first_argument_type,
                             typename Operation::result_type> {
   protected:
     Operation op;
     typename Operation::second_argument_type value;   // LOOK HERE
   public:
     binder2nd(const Operation& x,
               const typename Operation::second_argument_type& y)
         : op(x), value(y) {}
     typename Operation::result_type
     operator()(const typename Operation::first_argument_type& x) const {
       return op(x, value);
     }
   };

   template <class Operation, class T>
   inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
     typedef typename Operation::second_argument_type arg2_type;
     return binder2nd<Operation>(op, arg2_type(x));    // AND HERE
   }

and suppose I have a functor

   struct add : public binary_function<const int&,const int&, int> {
      int operator()( const int& x, const int& y ) const {
         return x+y;
      }
   };

This fails to compile due to the reference-to-a-reference problem.

Perhaps we decide that "add" is declared "correctly", though, and that
the ampersands sprinkled throughout the definition of bind(er)2nd should
be removed.  We could do this and get it to compile, but then
expressions like

   bind2nd( add(), 3+4 )

become awfully suspect, as now the resulting functor is trying to
"capture a reference to a temporary" (the "7") on the "LOOK HERE" line.

I've seen in many places a template for removing references, so that

   unreference<T>::type

will strip an ampersand off type T is there is one (and result in the
same type (unchanged) if there isn't).  Perhaps this should be done for
declaring "value" in binder2nd.  (But what if the function intended to
side-effect its second parameter via a non-const & ?) (Or perhaps the
"reference-to-reference" problem evidences a core language defect.)
Indeed, we could then use

   const unreference<typename Operation::second_argument_type>::type&

as the parameters, and then not worry about duplicating the ampersands.

Then there's the "AND HERE" line above.  I'm not sure why there is an
explicit conversion here.  Presumably this helps "coerce things along
the way"--maybe calling add() yourself in various non-template contexts
makes certain implicit conversions legal, but introducing bind2nd()
along the way requires explicit conversion.  I don't pretend to fully
understand.

Your original question doesn't address the issue, but while I'm
rambling I'd like to point out that binder2nd has a const operator().
What if I want to bind the 2nd argument of a non-const functor--what
then?


Ok, everything I've said above has been just syntax/how-do-I-
make-it-compile kinds of issues.  What about meaning?  What is
binary_function<X,Y,Z> supposed to mean?

The only thing I feel confident enough to say here is that in C++,
parameters passed "by value" and "by constant reference" are typically
two ways to say the same thing: this is an "in" parameter.  For the most
part, the two seem to differ only in performance but not in behavior
(unless your copy constructors have side-effects).  However, for
non-parameters, const T& and T mean different things (see for example
the problem with making a reference to the temporary "7" above).  In the
case of bind2nd above, it seems to me that it wants to store the bound
argument by value (for correctness) but take parameters by constant
reference (for performance).

And I think "by reference" may even impede performance if T happens to
be something small (like an int).  I haven't seen it, but I can imagine
a template specialization along the lines of

   efficient_parameter<T>::type

which has the logic

   if( sizeof(T) <= sizeof(void*) )     T
   else                                 const T&

which could be used for template function parameters.


A full answer to your question would have to deal with all of these
issues (ref-to-ref, constness, parameter efficiency, ...) and even maybe
some I haven't mentioned (what happens when you throw a "volatile" in
there?) and I would not be surprised if no one knows the full answer yet
(if there is one).  In any case, I think it's a "deeper" issue than it
may appear at first glance.  My hunch is that "binary_function" may be
"too coarse an abstraction" (e.g. perhaps "binary function whose first
parameter is 'in' and second parameter is 'in-out' is the right level of
granularity).

I'll be very interested to see what others have to say in this thread,
though.  :)

--
Brian McNamara

---
[ 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              ]






Author: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/06/02
Raw View
In article <8h42gk$dat$1@news-int.gatech.edu>, vaps4bm@prism.gatech.edu
(Brian McNamara!) wrote:

> I've seen in many places a template for removing references, so that
>
>    unreference<T>::type
>
> will strip an ampersand off type T is there is one (and result in the
> same type (unchanged) if there isn't).
<snip>
> And I think "by reference" may even impede performance if T happens to
> be something small (like an int).  I haven't seen it, but I can imagine
> a template specialization along the lines of
>
>    efficient_parameter<T>::type
>
> which has the logic
>
>    if( sizeof(T) <= sizeof(void*) )     T
>    else                                 const T&
>
> which could be used for template function parameters.

Interesting thoughts.  You've pretty much described "call_traits" on the
boost site:  http://www.boost.org/libs/utility/call_traits.htm

Not too surprising.  When a good idea's time has come, it often crops up
all over the place.

> I'll be very interested to see what others have to say in this thread,
> though.  :)

There's also work being done with binder2nd similar to what you describe,
although I think it is still in the review stage (not posted yet).

-Howard

---
[ 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              ]






Author: sirwillard@my-deja.com
Date: 2000/06/02
Raw View
In article <8h42gk$dat$1@news-int.gatech.edu>,
  vaps4bm@prism.gatech.edu (Brian McNamara!) wrote:
> smeyers@aristeia.com (Scott Meyers) once said:
> >In general, type "T" is not identical to type "const T&", so why
isn't
> >"const value_type&" passed to binary_function in the above example?

I'd say this is because of short sightedness.  This little gem has led
to a lot of "hacks" and "work arounds" in current code, and IMHO shows
yet another design defect in the standard functors.

> >Again, then, I ask: if I inherit from std::binary_function<Arg1Type,
> >Arg2Type, ReturnType> (or std::unary_function), what types should I
pass?

You should pass Arg1Type, etc.  The fact that it accepts a const
reference is arbitrary.  Yes, it can lead to problems, as evidenced in
the discussion below, but it's the only real answer available.  A
better design would probably still use a pass by const-reference
approach when appropriate, but would deal with cases where this isn't
appropriate cleanly, efficiently and transparently.

> I don't think I know "the answer" to the question, but this is one
I've
> thought about before, and I can at least tell you some of the issues
> I've considered.

I don't know the full answer yet either, but I'm working on this very
idea at the moment.  I can point out some alternative designs that
address the issue already, I just can't tell you if they've met all of
the demands yet.  Only time and use will tell this.

> Consider bind2nd (code from gcc's implementation):
>
>    template <class Operation>
>    class binder2nd
>      : public unary_function<typename Operation::first_argument_type,
>                              typename Operation::result_type> {
>    protected:
>      Operation op;
>      typename Operation::second_argument_type value;   // LOOK HERE
>    public:
>      binder2nd(const Operation& x,
>                const typename Operation::second_argument_type& y)
>          : op(x), value(y) {}
>      typename Operation::result_type
>      operator()(const typename Operation::first_argument_type& x)
const {
>        return op(x, value);
>      }
>    };
>
>    template <class Operation, class T>
>    inline binder2nd<Operation> bind2nd(const Operation& op, const T&
x) {
>      typedef typename Operation::second_argument_type arg2_type;
>      return binder2nd<Operation>(op, arg2_type(x));    // AND HERE
>    }
>
> and suppose I have a functor
>
>    struct add : public binary_function<const int&,const int&, int> {
>       int operator()( const int& x, const int& y ) const {
>          return x+y;
>       }
>    };
>
> This fails to compile due to the reference-to-a-reference problem.
>
> Perhaps we decide that "add" is declared "correctly", though, and that
> the ampersands sprinkled throughout the definition of bind(er)2nd
should
> be removed.  We could do this and get it to compile, but then
> expressions like
>
>    bind2nd( add(), 3+4 )
>
> become awfully suspect, as now the resulting functor is trying to
> "capture a reference to a temporary" (the "7") on the "LOOK HERE"
line.
>
> I've seen in many places a template for removing references, so that
>
>    unreference<T>::type
>
> will strip an ampersand off type T is there is one (and result in the
> same type (unchanged) if there isn't).  Perhaps this should be done
for
> declaring "value" in binder2nd.  (But what if the function intended to
> side-effect its second parameter via a non-const & ?) (Or perhaps the
> "reference-to-reference" problem evidences a core language defect.)
> Indeed, we could then use
>
>    const unreference<typename Operation::second_argument_type>::type&
>
> as the parameters, and then not worry about duplicating the
ampersands.
>
> Then there's the "AND HERE" line above.  I'm not sure why there is an
> explicit conversion here.  Presumably this helps "coerce things along
> the way"--maybe calling add() yourself in various non-template
contexts
> makes certain implicit conversions legal, but introducing bind2nd()
> along the way requires explicit conversion.  I don't pretend to fully
> understand.
>
> Your original question doesn't address the issue, but while I'm
> rambling I'd like to point out that binder2nd has a const operator().
> What if I want to bind the 2nd argument of a non-const functor--what
> then?
>
> Ok, everything I've said above has been just syntax/how-do-I-
> make-it-compile kinds of issues.  What about meaning?  What is
> binary_function<X,Y,Z> supposed to mean?
>
> The only thing I feel confident enough to say here is that in C++,
> parameters passed "by value" and "by constant reference" are typically
> two ways to say the same thing: this is an "in" parameter.  For the
most
> part, the two seem to differ only in performance but not in behavior
> (unless your copy constructors have side-effects).  However, for
> non-parameters, const T& and T mean different things (see for example
> the problem with making a reference to the temporary "7" above).  In
the
> case of bind2nd above, it seems to me that it wants to store the bound
> argument by value (for correctness) but take parameters by constant
> reference (for performance).
>
> And I think "by reference" may even impede performance if T happens to
> be something small (like an int).  I haven't seen it, but I can
imagine
> a template specialization along the lines of
>
>    efficient_parameter<T>::type
>
> which has the logic
>
>    if( sizeof(T) <= sizeof(void*) )     T
>    else                                 const T&
>
> which could be used for template function parameters.
>
> A full answer to your question would have to deal with all of these
> issues (ref-to-ref, constness, parameter efficiency, ...) and even
maybe
> some I haven't mentioned (what happens when you throw a "volatile" in
> there?) and I would not be surprised if no one knows the full answer
yet
> (if there is one).  In any case, I think it's a "deeper" issue than it
> may appear at first glance.  My hunch is that "binary_function" may be
> "too coarse an abstraction" (e.g. perhaps "binary function whose first
> parameter is 'in' and second parameter is 'in-out' is the right level
of
> granularity).
>
> I'll be very interested to see what others have to say in this thread,
> though.  :)

You summarized everything here very nicely.  You should know that
there's an implementation of the standard functor classes included in
the Boost library (http://www.boost.org).  These classes are in a
seperate namespace, meant to be used as alternatives to the standard
functors, not as actual implementations of them.  In other words, they
don't have to rely on any requirements set forth in the standard.  They
make use of a call_traits template that does all of the magic you
talked about, such as handling the double reference delema as well as
deciding if a const& approach is even the more efficient approach to
take.  As an added bonus, they use traits to determine the types
involved in order to eliminate the need for ptr_fun!  These replacement
functors can be used in many places where the standard functors can not
be.  I'm not sure they've addressed all type laundering issues, but
they've covered enough that only time and use is likely to find weak
spots.  The only "problem" with them is that they require partial
template specialization, which my compiler doesn't support yet. :(

--
William E. Kempf
Software Engineer, MS Windows Programmer


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ 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              ]






Author: smeyers@aristeia.com (Scott Meyers)
Date: 2000/06/02
Raw View
On Thu, 1 Jun 2000 05:00:40 CST, Chris Riesbeck wrote:
> How silly was I?

Well, from what I can tell, you didn't really offer an answer to my question.

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ 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              ]