This question also applies to boost::function
and std::tr1::function
.
std::function
is not equality comparable:
#include <functional>
void foo() { }
int main() {
std::function<void()> f(foo), g(foo);
bool are_equal(f == g); // Error: f and g are not equality comparable
}
In C++11, the operator==
and operator!=
overloads just don't exist. In an early C++11 draft, the overloads were declared as deleted with the comment (N3092 §20.8.14.2):
// deleted overloads close possible hole in the type system
It does not say what the "possible hole in the type system" is. In TR1 and Boost, the overloads are declared but not defined. The TR1 specification comments (N1836 §3.7.2.6):
These member functions shall be left undefined.
[Note: the boolean-like conversion opens a loophole whereby two function instances can be compared via
==
or!=
. These undefinedvoid
operators close the loophole and ensure a compile-time error. —end note]
My understanding of the "loophole" is that if we have a bool
conversion function, that conversion may be used in equality comparisons (and in other circumstances):
struct S {
operator bool() { return false; }
};
int main() {
S a, b;
bool are_equal(a == b); // Uses operator bool on a and b! Oh no!
}
I was under the impression that the safe-bool idiom in C++03 and the use of an explicit conversion function in C++11 was used to avoid this "loophole." Boost and TR1 both use the safe-bool idiom in function
and C++11 makes the bool
conversion function explicit.
As an example of a class that has both, std::shared_ptr
both has an explicit bool
conversion function and is equality comparable.
Why is std::function
not equality comparable? What is the "possible hole in the type system?" How is it different from std::shared_ptr
?
I think main reason is that if it would be, then it can't be used with non equality comparable types, even if equality comparison is never performed.
I.e. code that performs comparison should be instantiated early - at time when callable object is stored into std::function, for instance in one of constructors or assignment operators.
Such limitation would greatly narrow the scope of application, and obviously not acceptable for "general-purpose polymorphic function wrapper".
It is improtant to note, that it is possible to compare boost::function with callable object (but not with another boost::function)
This is possible, because function that performs such comparison is instantiniated at point of comparison, based on know operand type.
Moreover, std::function has target template member function, which can be used to perform similar comparison. In fact boost::function's comparison operators are implemented in terms of target member function.
So, there are no technical barriers which block implementantion of function_comparable.
Among answers there is common "impossible in general" pattern:
I completely disagree with this: it is not job of std::function to perform comparison itself, it's job is just to redirect request to comparison to underlying objects - that's all.
If underlying object type does not define comparison - it will be compile error in any case, std::function is not required to deduce comparison algorithm.
If underlying object type defines comparison, but which works wrong, or have some unusual semantic - it is not problem of std::function itself either, but it is problem of underlying type.
It is possible to implement function_comparable based on std::function.
Here is proof-of-concept:
There is some nice property - function_comparable can be compared against std::function too.
For instance, let's say we have vector of std::function's, and we want to give for user register_callback and unregister_callback functions. Using of function_comparable is required only for unregister_callback parameter:
Live demo at Ideone
Source code of demo:
Output is:
P.S. It seems like with help of std::type_index it is possible to implement similar to function_comparable class, which also supports ordering(i.e. less) or even hashing. But not only ordering between different types, but also ordering within same type (this requires support from types, like LessThanComparable).
the least that could be done is if std::function saves the address of the function used for binding to a string and used string comparison instead.
Actually, you can compare targets. It may work depends of what you want from comparison.
Here the code with inequality, but you can see how it works:
Ups, it is only valid since C++11.
std::function
is a wrapper for arbitrary callable types, so in order to implement equality comparison at all, you'd have to require that all callable types be equality-comparible, placing a burden on anyone implementing a function object. Even then, you'd get a narrow concept of equality, as equivalent functions would compare unequal if (for example) they were constructed by binding arguments in a different order. I believe it's impossible to test for equivalence in the general case.I would guess this means it's easier to delete the operators, and know for certain that using them will never give valid code, than to prove there's no possibility of unwanted implicit conversions occurring in some previously undiscovered corner case.
std::shared_ptr
has well-defined equality semantics; two pointers are equal if and only if they are either both empty, or both non-empty and pointing to the same object.This is thoroughly discussed in the Boost.Function FAQ. :-)
I may be wrong, but I think that equality of
std::function
objects is unfortunately not solvable in the generic sense. For example:are
f1
andf2
equal? What if I add an arbitrary number of function objects which simply wrap each other in various ways which eventually boils down to a call tof
... still equal?