Boost logo

Boost :

From: Brian McNamara (lorgon_at_[hidden])
Date: 2004-02-19 22:35:12


On Fri, Feb 20, 2004 at 01:25:59AM +0200, Peter Dimov wrote:
> Brian McNamara wrote:
> > Passing a non-ref-wrapped argument to a functoid that expects that
> > argument by-reference is a compile-time error.
...
> > Here's what we end up with
> >
> > int x;
> > app(_,3)(f); // fine (works now)
> > app(_,x)(f); // fine (works now)
> > app(_,3)(g); // compile-time error
> > app(_,x)(g); // compile-time error // (1)
> > app(_,ref(x))(g); // works
...
> > There are two main ways this works different from boost::lambda.
> > At (1) (and maybe also the line above), boost would create a copy and
> > then modify the copy. In the system I envision, this would be an
> > error.
>
> There are some interesting subtleties here. In Lambda (and an earlier
> version of Bind), (1) will create a _const_ copy and then fail to call g.
> The current Bind, however, behaves as you describe since it follows the TR1
> proposal more closely.
>
> The TR1 bind specification says that "if the function application is made
> through a cv-qualified reference to, or a copy of, the function object, the
> same cv qualifiers are applied to f and aN."
>
> In our case, since (the equivalent of) app(_, x) is non-const, its state,
> which includes the copy of x, can be changed. But if you pass app(_, x) by
> const reference to some algorithm, app(_, x)(g) will fail.
>
> > (This is the desirable behavior, IMO.)
>
> Maybe. I'd be interested in some rationale, though. :-)

Here's my rationale; tell me what you think:

If you have a reference parameter, it's because you want to have an
effect on an argument. Examples are "in-out" or "out" parameters, where
the parameter value is changed/assigned by the function so that the new
value can be used by the caller.

As a result, passing a copy of the argument and having the function
modify the copy (as in some cases in boost::lambda) means that the
modification gets "lost". In the example

   app(_,x)(g); // copy x, pass copy to g

"g" is oblivious to the fact that it's not being called as just

   g(x);

Whatever side-effects "g" makes to its argument never make it back to the
caller, since "app" is passing it a copy of "x". The "lost" information
probably indicates an error in the program.

As a result, I think it's preferable that

   app(_,x)(g);

should be a compile-time error, rather than copy "x" and pass the copy
to "g". The call

   app(_,ref(x))(g);

lets the caller see the effect on "x". If the client wants to
explicitly ignore the "result" of g, he can always do

   X dummy(x);
   app(_,ref(dummy))(g); // modifies the dummy

or some such. (My hunch is that this is very rare; you don't typically
"try to ignore" reference parameter modifications.)

What do you think?

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