Boost logo

Boost :

From: Brian McNamara (lorgon_at_[hidden])
Date: 2004-02-19 13:31:40


On Thu, Feb 19, 2004 at 03:46:01PM +0200, Peter Dimov wrote:
> Brian McNamara wrote:
> > That is, you cannot write "app" so that both calls here:
> >
> > int f(int);
> > int g(int&);
> > int x;
> > ...
> > app(_,3)(f);
> > app(_,x)(g);
> >
> > work properly. It's impossible.
>
> Let's see if I get this right:
>
> #include <boost/bind.hpp>
> #include <boost/bind/apply.hpp>
> #include <iostream>
>
> int f(int x)
> {
> std::cout << "f(" << x << ")\n";
> return x;
> }
>
> int g(int & x)
> {
> std::cout << "g(" << x << ")\n";
> return ++x;
> }
>
> int main()
> {
> int x = 2;
>
> // app(_, 3)(f);
>
> boost::bind( boost::apply<int>(), _1, 3 )(f);
>
> // app(_, x)(g);
>
> boost::bind( boost::apply<int>(), _1, x )(g);
> boost::bind( boost::apply<int>(), _1, boost::ref(x) )(g);
> }
>
> You are saying that in the general case we can't decide between the last two
> lines, so we shouldn't support references.

In a nutshell, yes.

> But I'm not sure that this is true. It seems to me that the rule is
> that I should use ref(x) if my x will outlive the function object, as in
> the above, and a plain x otherwise, and it will "just work".

This is not the rule I desire (though it is one possible way to go).
I don't like this rule because it is deciding "whether to reference or
not" based on the argument, rather than on the function. That is, in
the case of

   app(_, x)(f);

I don't want to pass "x" by reference simply because it appears be be a
non-transient object with sufficient lifetime. "f" doesn't need a
reference, so I shouldn't be creating one.

In any case, the question of "if my x will outlive the function object"
is non-trivial. Consider

   // assume h is a functoid with signature int h(int&,int)
   // assume l is a list<int>
   {
      int x = 1;
      l = map( h(x), l ); // note that "h(x)" means "h(x,_)"
   }

Does "x" outlive the function object? Is it reasonable to pass "x" by
reference?

The answer to both is no. Functional programming idioms often create
less-than-obvious lifetime issues. In the case above, the function
object outlives "x", because "l" is a lazy list and it stores the
function object so that list elements can later be computed on-demand.

> > boost::lambda's "solution" is to always take reference parameters;
> > this is why we have
> >
> > (_1 + _2)( 5, 7 ) // no, not 12--it's illegal
>
> No, not correct. In the bind equivalent to app(_, x)(f), x is always copied
> by default. It's f that is passed by reference.

Right, sorry; I was unclear. The function objects created via lambda
expressions always take non-const reference parameters. In contrast,
FC++ function objects always take const reference parameters.
(And, in both libraries, partial application makes a copy of the bound
arguments.)

(Is this right?)

-- 
-Brian McNamara (lorgon_at_[hidden])

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk