
On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
Daniel Walker wrote:
On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
Yes, this is one of the reasons why boost::bind( ... ) == 5 is valid, but _1 == 5 is not (the other being that given placeholder interoperability, both Bind and Lambda will claim _1 == 5 as their own.)
Right. As long as lambda has it's own placeholders you'll need to disambiguate them with namespace qualification or using directives.
No, this wasn't what I had in mind.
Consider what would happen if boost::bind supplies a "proper" operator==( a, b ) that attempts to return bind<bool>( __equal_to(), a, b ) and makes it work not just when a or b is a bind() expression (as is the case today), but for placeholders as well.
Lambda also has its operator== which does a similar thing, except that it returns a lambda object.
If Lambda starts recognizing Bind's placeholders as its own, the two operator== overloads (if visible) will both match ::_1 == 5. A similar ambiguity would occur with ll::_1 == 5 if Bind recognizes ll::_1 as a placeholder (which it already does).
OK, I see what you mean. That is an interesting problem. You're talking about a delayed equality comparison that would be invoked like (_1 == _2)(a, b) or (_1 == 5)(a), essentially providing some lambda-expression-like functionality in Boost.Bind. There would also be a problem if standard library implementers made their placeholders EqualityComparable, like _1 == _2 to distinguish placeholder _1 from _2 and not the arguments bound to placeholders _1 and _2. This idea might occur to some vendors and seem like a good one, but it would break lambda expressions. This gets to the more general problem of combining objects with overloaded operators in the same expression. Boost.Parameter has the same problem when operator|| is used with a lambda expression. As Dave pointed out a while back, one way around this problem is to "unlambda" a subexpression when you want lambda's operators to stop being applied. However, if the expression containing a call to unlambda also contains placeholders, you're back where you started. I haven't worked out the details yet, but I think you could make a function analogous to unlambda except it would only apply to placeholders and would tell lambda to ignore the placeholder, i.e. to decline to use the placeholder to hold the place of an argument and give 3rd-parties the oportunity to use the placeholder. You could call the function lambda::release_place or just lambda::release. There could be an inverse lambda::hold function that would force the placeholder to be used by lambda. If these functions generated proxies that would disable or enable lambda's operator overloads and if the proxy generated by lambda::release was recognizable as a placeholder by 3rd-parties (either by convertibility to TR1 placeholders or a specialization of is_placeholder) ... then these functions could disambiguate the expression and let the user specify who gets to use the placeholder, lambda or someone else. It could look like ... _1 == _2 // ambiguous lambda::hold(_1) == _2 // lambda expression lambda::release(_1) == _2 // 3rd-party expression Of course, this is only necessary when TR1 implementations or 3rd-parties overload operators for placeholders, and they're used in conjunction with lambda expressions. Still, you're bringing up an important point for placeholder interoperability. I may be able to give a snippet of code for lambda::release tomorrow. lambda::hold is easy. For _1 it might look like ... template<class T> typename enable_if< mpl::equal_to< mpl::int_<std::tr1::is_placeholder<T>::value> , mpl::int_<1> > , lambda::placeholder1_type
::type hold(T) { return lambda::_1; }
Daniel