On bind::operator==, again

What do you think this expression should do: bind(f, _1) == bind(g, _1) Currently, boost::bind returns f == g when f and g are of the same type, and fails at compile type otherwise (this is the equality comparison feature Doug requested). lambda::bind returns lambda(x) { return f(x) == g(x) }. It is clear that a future boost::bind that has limited lambda support would be even more problematic (as would be a future equality comparable boost.lambda). In that case, the expression will always compile, but do something different depending on whether f and g are of the same type. -- Peter Dimov http://www.pdimov.com

On Tue, Mar 09, 2004 at 05:00:33PM +0200, Peter Dimov wrote:
What do you think this expression should do:
bind(f, _1) == bind(g, _1)
Currently, boost::bind returns f == g when f and g are of the same type, and fails at compile type otherwise (this is the equality comparison feature Doug requested).
lambda::bind returns lambda(x) { return f(x) == g(x) }.
It is clear that a future boost::bind that has limited lambda support would be even more problematic (as would be a future equality comparable boost.lambda). In that case, the expression will always compile, but do something different depending on whether f and g are of the same type.
In my opinion, lambda(x) { return f(x) == g(x) }. is best. Whenever I see a placeholder, I expect the whole expression (modulo operator precedence and "constant(foo)" issues) to get lambda-ized, and thus I expect operator== to create a lambda expression. (I dunno if this is the 'right' expectation to have, given the way the libraries work now, but I think it is a reasonable/simple 'ideal' expectation.) More generally, I think it's not a good idea to try to create "function equality" with operator==. I didn't participate in any of the earlier discussions, but I think that if you want to register/unregister function objects with an event handler, for instance, you should use a separate "handle" object to keep track of "function object identity". (The whole idea of "f==g" being meaningful for two functions is a little bit fishy to me, but this is probably more of a personal bias than anything I can defend objectively.) -- -Brian McNamara (lorgon@cc.gatech.edu)

On Tuesday 09 March 2004 05:26 pm, Brian McNamara wrote:
In my opinion, lambda(x) { return f(x) == g(x) }. is best. Whenever I see a placeholder, I expect the whole expression (modulo operator precedence and "constant(foo)" issues) to get lambda-ized, and thus I expect operator== to create a lambda expression. (I dunno if this is the 'right' expectation to have, given the way the libraries work now, but I think it is a reasonable/simple 'ideal' expectation.)
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
More generally, I think it's not a good idea to try to create "function equality" with operator==. I didn't participate in any of the earlier discussions, but I think that if you want to register/unregister function objects with an event handler, for instance, you should use a separate "handle" object to keep track of "function object identity".
The problem is that there are a whole lot of use cases for equality of function objects, and people really want them. The especially want delegate<void()> f; f += some_function_object; // connect f -= some_function_object; // disconnect
(The whole idea of "f==g" being meaningful for two functions is a little bit fishy to me, but this is probably more of a personal bias than anything I can defend objectively.)
I held to that philosophy for a long time, but the use cases are stacked against us :) Doug

Douglas Gregor wrote:
On Tuesday 09 March 2004 05:26 pm, Brian McNamara wrote:
In my opinion, lambda(x) { return f(x) == g(x) }. is best. Whenever I see a placeholder, I expect the whole expression (modulo operator precedence and "constant(foo)" issues) to get lambda-ized, and thus I expect operator== to create a lambda expression. (I dunno if this is the 'right' expectation to have, given the way the libraries work now, but I think it is a reasonable/simple 'ideal' expectation.)
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory: #include <boost/bind.hpp> namespace boost { namespace _bi { struct equal { template<class V, class W> bool operator()(V const & v, W const & w) const { return v == w; } }; template<class R, class F, class L, class A2> bind_t< bool, equal, list2< bind_t<R, F, L>, typename add_value<A2>::type > > operator== (bind_t<R, F, L> const & f, A2 a2) { typedef list2< bind_t<R, F, L>, typename add_value<A2>::type > list_type; return bind_t<bool, equal, list_type> ( equal(), list_type(f, a2) ); } } // namespace _bi } // namespace boost #include <iostream> int f(int x) { return x + x; } long g(int x) { return 2 * x; } int main() { int x = 4; std::cout << ( boost::bind(f, _1) == 8 )(x) << std::endl; std::cout << ( boost::bind(f, _1) == boost::bind(g, _1) )(x) << std::endl; }
More generally, I think it's not a good idea to try to create "function equality" with operator==. I didn't participate in any of the earlier discussions, but I think that if you want to register/unregister function objects with an event handler, for instance, you should use a separate "handle" object to keep track of "function object identity".
The problem is that there are a whole lot of use cases for equality of function objects, and people really want them. The especially want
delegate<void()> f; f += some_function_object; // connect f -= some_function_object; // disconnect
There is no mention of operator== anywhere in the above, though.

"Peter Dimov" <pdimov@mmltd.net> writes:
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory:
Really? I didn't figure out what the code illustrates, but my recent attempts to use Lambda have been a dismal failure, mostly because of subtle (and sometimes not-so-subtle) differences with Bind. For example, Lambda doesn't seem to support get_pointer to dereference through smart pointers. The tuple/reference issue I posted about yesterday was another obstacle. I'm rather discouraged with how difficult it is to use standard algorithms -- or even algorithms over sequences as opposed to iterators -- to write something that could be coded quickly using dumb loops. I hold out some hope that the long-promised integration of Lambda and Phoenix will help to solve these problems, but it's hard to tell when (or if) that's actually coming. Glum in Somerville, Dave

David Abrahams wrote:
"Peter Dimov" <pdimov@mmltd.net> writes:
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory:
Really? I didn't figure out what the code illustrates, ...
The code illustrates that Bind can be easily enhanced to have limited lambda support.
... but my recent attempts to use Lambda have been a dismal failure, mostly because of subtle (and sometimes not-so-subtle) differences with Bind. For example, Lambda doesn't seem to support get_pointer to dereference through smart pointers. The tuple/reference issue I posted about yesterday was another obstacle.
You are just repeating my statement. There should be no differences.

On Wednesday 10 March 2004 07:41 am, Peter Dimov wrote:
Douglas Gregor wrote:
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory:
Having Lambda's == and != operators return function objects convertible to bool would keep Lambda's semantics a superset of Bind's semantics.
More generally, I think it's not a good idea to try to create "function equality" with operator==. I didn't participate in any of the earlier discussions, but I think that if you want to register/unregister function objects with an event handler, for instance, you should use a separate "handle" object to keep track of "function object identity".
The problem is that there are a whole lot of use cases for equality of function objects, and people really want them. The especially want
delegate<void()> f; f += some_function_object; // connect f -= some_function_object; // disconnect
There is no mention of operator== anywhere in the above, though.
Right. The basic idea of "-=" is that it uses == to find connected targets equal to some_function_object and disconnects them. Of course, it could use function_equal or anything else, but I do feel that == is most natural. Doug

Douglas Gregor wrote:
On Wednesday 10 March 2004 07:41 am, Peter Dimov wrote:
Douglas Gregor wrote:
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory:
Having Lambda's == and != operators return function objects convertible to bool would keep Lambda's semantics a superset of Bind's semantics.
I find your suggestion intriguing and appealing, in a certain "clever hack" way, but you miss my point. I am not talking about keeping Lambda compatible with what Bind does now. What I have in mind is an extended Bind that supports !bind, bind ==, bind !=. That Bind is what I want to be compatible with Lambda. At the moment, your idea seems too costly to implement in Bind, and I see no corresponding benefits, but I'll keep it in mind as a potential alternative.

On Thursday 11 March 2004 06:19 am, Peter Dimov wrote:
Douglas Gregor wrote:
On Wednesday 10 March 2004 07:41 am, Peter Dimov wrote:
Douglas Gregor wrote:
I'd think that this is the right expectation for Lambda, but not for FC++ or Bind. Not that I'm helping any :)
No, I don't think so. Expressions that are valid for both Bind and Lambda should have the same semantics. And in fact, this is not just theory:
Having Lambda's == and != operators return function objects convertible to bool would keep Lambda's semantics a superset of Bind's semantics.
I find your suggestion intriguing and appealing, in a certain "clever hack" way, but you miss my point. I am not talking about keeping Lambda compatible with what Bind does now. What I have in mind is an extended Bind that supports !bind, bind ==, bind !=. That Bind is what I want to be compatible with Lambda. At the moment, your idea seems too costly to implement in Bind, and I see no corresponding benefits, but I'll keep it in mind as a potential alternative.
It's not really an alternative. We have two possible reasons that a binding operator would want to overload operator==: for comparison or for creating a function object. I'm just saying we don't have to choose between the alternatives, because we can overload operator== to return a function object that also has a conversion to bool that compares the stored function objects. The benefit is that we don't need to introduce a "function_equal" to compare functions and we can still choose whether to support (bind(f, _1) == bind(g, _1))(5) Doug

Douglas Gregor wrote:
It's not really an alternative. We have two possible reasons that a binding operator would want to overload operator==: for comparison or for creating a function object. I'm just saying we don't have to choose between the alternatives, because we can overload operator== to return a function object that also has a conversion to bool that compares the stored function objects.
Yep, unfortunately not all of us can, but let's keep implementation considerations out of it for now.
The benefit is that we don't need to introduce a "function_equal" to compare functions and we can still choose whether to support (bind(f, _1) == bind(g, _1))(5)
But there's still the implicit assumption that not introducing function_equal is a benefit. I don't see why this should be so clear cut. What particular problems do you see with function_equal?

On Thursday 11 March 2004 10:27 am, Peter Dimov wrote:
Douglas Gregor wrote:
The benefit is that we don't need to introduce a "function_equal" to compare functions and we can still choose whether to support (bind(f, _1) == bind(g, _1))(5)
But there's still the implicit assumption that not introducing function_equal is a benefit. I don't see why this should be so clear cut. What particular problems do you see with function_equal?
I don't use string_equal for strings or shared_ptr_equal for shared_ptr's, so why should I do so for function objects? Function objects are objects, they just happen to have a function call operator. The only reason I can see for us avoiding using operator== is that Lambda has already hijacked this operator for something entirely different. That doesn't seem like a great reason to force everyone else using other libraries to use "function_equal" instead of the more natural operator==. We're only allowing bind(...) == bind(...) when the types are equivalent, right? So we're only realy worried about the very, very narrow case where we have lambda-expr-1 == lambda-expr-2 and the two operands have exactly the same type. We have a way to deal with this case using an implicit conversion to bool, so why does it weigh so heavily on our minds? Or is there another reason to avoid operator==? Doug

Douglas Gregor wrote:
On Thursday 11 March 2004 10:27 am, Peter Dimov wrote:
Douglas Gregor wrote:
The benefit is that we don't need to introduce a "function_equal" to compare functions and we can still choose whether to support (bind(f, _1) == bind(g, _1))(5)
But there's still the implicit assumption that not introducing function_equal is a benefit. I don't see why this should be so clear cut. What particular problems do you see with function_equal?
I don't use string_equal for strings or shared_ptr_equal for shared_ptr's, so why should I do so for function objects?
Wait a minute. Which "I" are we speaking of here? function<> will use function_equal, but the end users will not be required to even know about its existence. If they want to define operator== for their function objects and then compare them for equality, they are perfectly welcome.

On Thursday 11 March 2004 04:37 pm, Peter Dimov wrote:
Douglas Gregor wrote:
On Thursday 11 March 2004 10:27 am, Peter Dimov wrote:
Douglas Gregor wrote:
The benefit is that we don't need to introduce a "function_equal" to compare functions and we can still choose whether to support (bind(f, _1) == bind(g, _1))(5)
But there's still the implicit assumption that not introducing function_equal is a benefit. I don't see why this should be so clear cut. What particular problems do you see with function_equal?
I don't use string_equal for strings or shared_ptr_equal for shared_ptr's, so why should I do so for function objects?
Wait a minute. Which "I" are we speaking of here? function<> will use function_equal, but the end users will not be required to even know about its existence. If they want to define operator== for their function objects and then compare them for equality, they are perfectly welcome.
"I" as a user, that wants to compare two function objects that are, say, returned from bind. Anyway, I'm going to back off on this. We'll define function_equal to (by default) use operator==, and overload it for bind/lambda/fc++/mem_fn/etc. function objects to do the actual comparison. Function's operator== (or contains, or whatever survives the discussion in our other thread) will use function_equal. If users are clamoring for the ability to compare function objects returned by bind and whining about using function_equal, we can revisit it down the road. Doug

On Wed, Mar 10, 2004 at 12:08:30AM -0500, Douglas Gregor wrote:
The problem is that there are a whole lot of use cases for equality of function objects, and people really want them. The especially want
delegate<void()> f; f += some_function_object; // connect f -= some_function_object; // disconnect
I don't see why people can't cope with delegate<void()> f; handle h = f.connect( some_function_object ); ... f.disconnect( h ); instead. Indeed, it seems to me that it might be cheaper to hold onto the handle in some cases rather than the function object. (A handle could presumably be an int, whereas a function object may be many words of data or maybe even require heap storage.) But admittedly, I don't typically work in these domains to know how people _actually_ use (or want to use) these things. Maybe the best compromise is to define a new entity for "function equality". Define a function (result_of-capable functor, really) like boost::function_equal( f, g ) which has this behavior: - if f and g are both function pointers, use operator==, else - if one or both are user-defined types, call f.function_equal(g) (or g.function_equal(f)) if it exists, else - return false (or fail to compile, whichever people decide is best) Then lambda expressions (et al.) can define their own notion of function equality as they see fit, and it wouldn't conflict with the existing meaning of operator== in lambda expressions, and libraries (like signals) could choose to use boost::function_equal as a default predicate if they want (in any case, the existence of function_equal makes it easy to 'swap policies' with any algorithms that takes an equality predicate as an optional extra parameter), and you could then make delegate<void()> f; f += some_function_object; // connect f -= some_function_object; // disconnect work if people are really clamoring for it. -- -Brian McNamara (lorgon@cc.gatech.edu)

On Wednesday 10 March 2004 11:27 am, Brian McNamara wrote:
I don't see why people can't cope with
delegate<void()> f; handle h = f.connect( some_function_object ); ... f.disconnect( h );
instead. Indeed, it seems to me that it might be cheaper to hold onto the handle in some cases rather than the function object. (A handle could presumably be an int, whereas a function object may be many words of data or maybe even require heap storage.) But admittedly, I don't typically work in these domains to know how people _actually_ use (or want to use) these things.
Oh, I know, and I mostly agree. I tend to liken += and -= to new and delete, whereas the handle idiom (they're calling "connections" in Signals) is like having a smart pointer.
Maybe the best compromise is to define a new entity for "function equality". Define a function (result_of-capable functor, really) like
boost::function_equal( f, g )
which has this behavior:
- if f and g are both function pointers, use operator==, else
- if one or both are user-defined types, call f.function_equal(g) (or g.function_equal(f)) if it exists, else
- return false (or fail to compile, whichever people decide is best)
We can have boost::function_equal(f, g), but I'd prefer that it always use operator==. Then boost::lambda, boost::bind, etc. can just overload function_equal. Why? Because most users just want to write a normal operator== and have it work; that's what makes sense. Just because Lambda can do some screwy things with syntax (and believe me, I love Lambda) doesn't mean others should have to use a weird comparison syntax. Doug

Douglas Gregor wrote:
We can have boost::function_equal(f, g), but I'd prefer that it always use operator==. Then boost::lambda, boost::bind, etc. can just overload function_equal.
That's what I had in mind when I posted; sorry for not mentioning it, but I didn't want to influence the answers. Am I not evil. In summary, unqualified function_equal(f, g), with a default implementation that returns f == g.
Why? Because most users just want to write a normal operator== and have it work; that's what makes sense. Just because Lambda can do some screwy things with syntax (and believe me, I love Lambda) doesn't mean others should have to use a weird comparison syntax.
I note that you show some pro-== bias. function_equal (my original name was fn_equal, but I can live with either) is not the same as ==, even though it does use == by default. Placeholders, reference_wrapper, tuple all respond differently to fn_equal and ==. In fact, I still think that your decision to expose the 'f contains g' operation under the f == g notation is a mistake, too. 'contains' is not '==', either; it is asymmetric, as it is possible for f to contain g but not vice versa. In short, == for function objects is an illusion and it must die, Herb Sutter's influential articles notwithstanding.

On Thursday 11 March 2004 06:14 am, Peter Dimov wrote:
Why? Because most users just want to write a normal operator== and have it work; that's what makes sense. Just because Lambda can do some screwy things with syntax (and believe me, I love Lambda) doesn't mean others should have to use a weird comparison syntax.
I note that you show some pro-== bias. function_equal (my original name was fn_equal, but I can live with either) is not the same as ==, even though it does use == by default. Placeholders, reference_wrapper, tuple all respond differently to fn_equal and ==.
In fact, I still think that your decision to expose the 'f contains g' operation under the f == g notation is a mistake, too. 'contains' is not '==', either; it is asymmetric, as it is possible for f to contain g but not vice versa.
In short, == for function objects is an illusion and it must die, Herb Sutter's influential articles notwithstanding.
Opinion noted :) The primary reason for using operator== is that it models what function pointers do, and makes function<> a near-complete drop-in replacement for function pointers. That's been my goal for a Very Long Time. The question is whether the ambiguities it brings outweigh the benefits of having a function-pointer---like syntax. Doug

Douglas Gregor wrote:
On Thursday 11 March 2004 06:14 am, Peter Dimov wrote:
In fact, I still think that your decision to expose the 'f contains g' operation under the f == g notation is a mistake, too. 'contains' is not '==', either; it is asymmetric, as it is possible for f to contain g but not vice versa.
In short, == for function objects is an illusion and it must die, Herb Sutter's influential articles notwithstanding.
Opinion noted :)
The primary reason for using operator== is that it models what function pointers do, and makes function<> a near-complete drop-in replacement for function pointers. That's been my goal for a Very Long Time.
Yes, I understand the Long-Term Goal (if not its utmost importance), but your approach doesn't scale to other function<>-like entites. void f(); my_function<void()> g(f); your_function<void()> h(g); g == h; //??? g.contains(h); // false h.contains(g); // true It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)

On Thu, Mar 11, 2004 at 05:35:38PM +0200, Peter Dimov wrote:
Yes, I understand the Long-Term Goal (if not its utmost importance), but your approach doesn't scale to other function<>-like entites.
void f(); my_function<void()> g(f); your_function<void()> h(g);
g == h; //??? g.contains(h); // false h.contains(g); // true
It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)
I am also curious about how cases like void f(int,int,int); function<void(int,int)> tmp1 = bind(&f,5,_1,_2); function<void(int,int)> tmp2 = bind(&f,_1,_2,7); bind(tmp1,_1,7) == bind(tmp2,5,_1) // ? (ought to) work. (If my intent isn't clear above, I am trying to bind the first and third arguments of f() in two different ways. Presumably the LHS and RHS of the operator== on the last line have different types, despite the fact that they "are the same function", for some definition of sameness.) -- -Brian McNamara (lorgon@cc.gatech.edu)

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Mar 11, 2004 at 05:35:38PM +0200, Peter Dimov wrote:
Yes, I understand the Long-Term Goal (if not its utmost importance), but your approach doesn't scale to other function<>-like entites.
void f(); my_function<void()> g(f); your_function<void()> h(g);
g == h; //??? g.contains(h); // false h.contains(g); // true
It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)
I am also curious about how cases like
void f(int,int,int); function<void(int,int)> tmp1 = bind(&f,5,_1,_2); function<void(int,int)> tmp2 = bind(&f,_1,_2,7); bind(tmp1,_1,7) == bind(tmp2,5,_1) // ?
(ought to) work. (If my intent isn't clear above, I am trying to bind the first and third arguments of f() in two different ways. Presumably the LHS and RHS of the operator== on the last line have different types, despite the fact that they "are the same function", for some definition of sameness.)
Just to throw more darts at the idea, building the composite function object could be an expensive way to arrive at an immediate bool result if copying the function objects was a nontrivial operation. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Thursday 11 March 2004 03:14 pm, David Abrahams wrote:
Just to throw more darts at the idea, building the composite function object could be an expensive way to arrive at an immediate bool result if copying the function objects was a nontrivial operation.
Thoughout the STL is the implicit assumption that function objects are cheap to copy. We're not making any new assumptions here. Doug

On Mar 11, 2004, at 4:06 PM, Douglas Gregor wrote:
On Thursday 11 March 2004 03:14 pm, David Abrahams wrote:
Just to throw more darts at the idea, building the composite function object could be an expensive way to arrive at an immediate bool result if copying the function objects was a nontrivial operation.
Thoughout the STL is the implicit assumption that function objects are cheap to copy. We're not making any new assumptions here.
Unfortunately I wouldn't classify function<...> as cheap to copy. It's not terribly expensive either, but it does call new during the copy ctor (unless I'm just looking at a flawed implementation - my own ;-) ). But bind should be cheap to copy as long as its contained functors are cheap. -Howard

On Thursday 11 March 2004 01:29 pm, Brian McNamara wrote:
I am also curious about how cases like
void f(int,int,int); function<void(int,int)> tmp1 = bind(&f,5,_1,_2); function<void(int,int)> tmp2 = bind(&f,_1,_2,7); bind(tmp1,_1,7) == bind(tmp2,5,_1) // ?
(ought to) work. (If my intent isn't clear above, I am trying to bind the first and third arguments of f() in two different ways. Presumably the LHS and RHS of the operator== on the last line have different types, despite the fact that they "are the same function", for some definition of sameness.)
It'll fail to compile. You can't compare boost::function objects to each other, only to targets of those function objects. Doug

On Thursday 11 March 2004 10:35 am, Peter Dimov wrote:
void f(); my_function<void()> g(f); your_function<void()> h(g);
g == h; //???
I'd bet that this results in a compilation error, so the user would know to try on of these syntaxes:
g.contains(h); // false h.contains(g); // true
It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)
Right, you can't compare function<> objects, but you can compare a function<> object to something it could target. Doug

Douglas Gregor wrote:
On Thursday 11 March 2004 10:35 am, Peter Dimov wrote:
void f(); my_function<void()> g(f); your_function<void()> h(g);
g == h; //???
I'd bet that this results in a compilation error, so the user would know to try on of these syntaxes:
g.contains(h); // false h.contains(g); // true
For some reason, I think that you are busy and don't have much time to read your e-mail. :-) The problem here is that you are establishing an idiom that represents 'contains' via the following signatures: template<...> bool operator==(function<...> const & f, G g); template<...> bool operator==(G g, function<...> const & f); My point is that this idiom is not applicable to other function<>-like classes, because if you also have template<...> bool operator==(my_function<...> const & f, G g); template<...> bool operator==(G g, my_function<...> const & f); you can neither ask a function<> whether it contains a my_function<>, nor can you ask a my_function<> whether it contains a function<>, as both classes claim the mixed operator== for _their_ 'contains' operation.
I'd bet that this results in a compilation error, so the user would know to try on of these syntaxes:
g.contains(h); // false h.contains(g); // true
Good luck, if the authors haven't provided 'contains' as a member.
It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)
Right, you can't compare function<> objects, but you can compare a function<> object to something it could target.
A function<> can target another function<>, can it not?

On Thursday 11 March 2004 04:49 pm, Peter Dimov wrote:
For some reason, I think that you are busy and don't have much time to read your e-mail. :-)
Ha ha! You are so very, very right :) But the fact that I have just a little time to read my e-mail is the real danger. Kind of like having just a little knowledge of explosives...
The problem here is that you are establishing an idiom that represents 'contains' via the following signatures:
template<...> bool operator==(function<...> const & f, G g); template<...> bool operator==(G g, function<...> const & f);
My point is that this idiom is not applicable to other function<>-like classes, because if you also have
template<...> bool operator==(my_function<...> const & f, G g); template<...> bool operator==(G g, my_function<...> const & f);
you can neither ask a function<> whether it contains a my_function<>, nor can you ask a my_function<> whether it contains a function<>, as both classes claim the mixed operator== for _their_ 'contains' operation.
I understand. This will result in a compilation error, because the operator== invocation would be ambiguous.
I'd bet that this results in a compilation error, so the user would
know to try on of these syntaxes:
g.contains(h); // false h.contains(g); // true
Good luck, if the authors haven't provided 'contains' as a member.
But any quality my_function<> will supply something like this, right?
It's limited to the one and only function<> (and you can't even ask a function<> whether it contains another function<>, right?)
Right, you can't compare function<> objects, but you can compare a function<> object to something it could target.
A function<> can target another function<>, can it not?
So long as the types between the <>'s for the two are different, yes. But as you know, you don't want to be using operator== to compare such things, because it isn't clear which way to perform the containment check, and most likely you really wanted to determine if the targets of the two function<> objects are equal (but you're out of luck). I actually did consider these issues before committing operator== for function<> objects. We have: function<FT> == function<FT>: still a compilation error, because we can't implement it. function<FT1> == function<FT2>: a compilation error, because it isn't clear which way to check for containment and you probably don't want to do it either way function<FT> == my_function<...>: a compilation error if my_function<...> has the same type of operator== that function does. Same rationale as the case above with different function<...> operations. function<FT> == F or F == function<FT>: checks containment The "contains" member is there precisely because I knew about the cases where we want to check containment but don't want to use operator==. I guess the above will either convince you that: 1) operator== is sufficiently limited so that it is safe to use, because bad uses end up with compiler errors. 2) operator== is still a terrible way to represent check the containment relationship, and turns into a bloody mess of requirements. I'm still on the side of #1, but do note that I have a monomaniacal obsession with making function<> able to completely, totally, and utterly replace function pointers and member function pointers with a minimum of effort. Low-level abstractions MUST DIE! Doug, slightly off the rocker

Douglas Gregor wrote: [...]
The "contains" member is there precisely because I knew about the cases where we want to check containment but don't want to use operator==.
I must be missing something. The "contains" member is there? Where? I don't see "contains" in the source, in the docs, and my compiler doesn't see it, either (it has much better vision).
I guess the above will either convince you that:
1) operator== is sufficiently limited so that it is safe to use, because bad uses end up with compiler errors. 2) operator== is still a terrible way to represent check the containment relationship, and turns into a bloody mess of requirements.
I don't view these two options as mutually exclusive. ;-)

On Thursday 11 March 2004 06:09 pm, Peter Dimov wrote:
Douglas Gregor wrote:
[...]
The "contains" member is there precisely because I knew about the cases where we want to check containment but don't want to use operator==.
I must be missing something. The "contains" member is there? Where? I don't see "contains" in the source, in the docs, and my compiler doesn't see it, either (it has much better vision).
Sorry, it's called "target", and it's not the same as contains. The contains we're talking about would be implemented as: // inside class template function/functionN template<typename F> bool contains(const F& f) const { if (const F* fp = this->template target<F>()) { return function_equal(*fp, f); } else { return false; } } I should (will) add it, regardless of the fate of operator==, because it's obviously needed.
I guess the above will either convince you that:
1) operator== is sufficiently limited so that it is safe to use, because bad uses end up with compiler errors. 2) operator== is still a terrible way to represent check the containment relationship, and turns into a bloody mess of requirements.
I don't view these two options as mutually exclusive. ;-)
Heh heh. Well, I'm all out of arguments for operator==. Doug

On Tuesday 09 March 2004 10:00 am, Peter Dimov wrote:
What do you think this expression should do:
bind(f, _1) == bind(g, _1)
Currently, boost::bind returns f == g when f and g are of the same type, and fails at compile type otherwise (this is the equality comparison feature Doug requested).
lambda::bind returns lambda(x) { return f(x) == g(x) }.
Well, the lambda function object returned by bind(f, _1) == bind(g, _1) could be convertible to bool. Too cute? It might just be slick enough to work. Doug
participants (5)
-
Brian McNamara
-
David Abrahams
-
Douglas Gregor
-
Howard Hinnant
-
Peter Dimov