
Eric Niebler:
OK, OK, you got me. Even for Phoenix, the rules are not simple. Phoenix captures by value *when it can* (except for arrays, which are by reference, I think), or when the user specifies by-ref with phoenix::ref. But the decision is based on object's type, not on how the object is used in the expression, as it is with boost.lambda. Consider that with boost.lambda:
int ii = 0; ii += _1; // ii captured by reference here ... _1 += ii; // ... but ii captured by value here
Right. As with std::cout, this is being done for usability reasons. The demand for op= to capture its left operand by value is nonexistent. I do not necessarily claim that Lambda is right. After all, boost::bind deliberately captures by value in situations in which it knows that by-ref is more common. Normally, I'd state that both Lambda and Phoenix need to pick one over the other and stick with it. But there is a problem with this as well in that it could affect the eventual compatibility with boost::bind. boost::bind dutifully propagates its constness to the bound object and its arguments. In bind( f, a )(), f and a are non-const, because bind(f,a) isn't. Phoenix doesn't. It always "constifies" its contents. This is a good thing for ii += _1 because it allows it to fail. Const propagation is less of an issue in Phoenix because it has true local variables. With boost::bind and a suitably defined f, one can do boost::bind( f, 0, 0, _1 ) to approximate a lambda with two local variables, initially 0, and one argument. Phoenix doesn't need such tricks. But the question needs to be considered, and a balance has to be struck. (It's also possible to only constify the left side of op=, which is also a bit of a hack, but would be a net usability win.)