Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2004-02-19 13:54:36


Brian McNamara wrote:
> On Thu, Feb 19, 2004 at 03:26:12PM +0200, Peter Dimov wrote:
>> Brian McNamara wrote:
>>> A number of people have voiced similar complaints, and I am
>>> gradually being worn down. I think one of the reasons I resist it
>>> is that I don't like how boost::lambda has dealt with the issue.
>>> Notably, examples like
>>>
>>> // From 5.3.2 of the lambda docs
>>> bind(&A::set_j, a, _1)(k); // a.j == 0, as a copy of a is modified
>>> bind(&A::set_j, _1, 1)(a); // a.j == 1
>>>
>>> where reference-versus-copy depends upon the precise "timing" of
>>> binding a function argument to a value, ...
>>
>> There is no "precise timing" issue here, the rule is simple. Every
>> argument passed to bind() is copied, whether it is &A::set_j, a, _1,
>> or 1.
>
> I do understand this.
>
> I am looking from another perspective. bind() lets me pass some of
> the arguments to a function "now", and the rest "later". In the
> example above, if I pass "a" now and "k" (1) later, I get one
> behavior, but if I pass 1 now and "a" later, I get another.
>
> This is counter-intuitive, from my perspective.

Perhaps this is a question of underlying philosophy. You are looking at
bind() from fc++ perspective.

Conceptually, bind() is not a two-stage argument pass device, but a function
adaptor device. It takes a function object as input and produces a different
function object as output. Consider for example bind(f, _2, _1), or bind(f,
_4), where no phased argument passing occurs.

If you look at bind() from the fc++ perspective, where currying and
invocation have the same syntax, it is understandable why the two "argument
passing modes" appear counter-intuitive. It is, however, self-consistent.

The bind() model of separate adaptation and invocation can handle the
question of object lifetime better (const reference vs non-const reference
is actually a distraction). Consider these two examples:

[1]

struct X {};

void g(X const & x, int);

function<void(int)> f()
{
    X x;
    return bind(g, x, _1);
}

[2]

struct X: private noncopyable
{
};

void g(X const & x, int);

template<class F> void h(F f);

void f()
{
    X x;
    h( bind(g, ref(x), _1 );
}

In both cases, everything is pure const, no side effects, but the by-value
vs by-reference difference is crucial.

While the unified syntax of fc++ is quite appealing, there is no bind/call
separation. Of course you could enable_if it to behave differently when one
of the arguments is _ but I'm not sure whether this would be a good idea.


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