
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.