Comparing std::functions for equality?
operator== for std::function compares a std::function with a null pointer, as far as I can tell the standard does not provide any details as to why.
Although, this boost FAQ entry, Why can't I compare boost::function objects with operator== or operator!=? provides a rationale and as far as I can tell should be applicable to std::function as well. Quoting the FAQ:
Comparison between boost::function objects cannot be implemented "well", and therefore will not be implemented. [...]
it then outlines requested solutions similar to Preet's and goes on to say:
The problem occurs when the type of the function objects stored by both f and g doesn't have an operator==[...]
and explains why this has to has to be dealt with in either the assignment operator or constructor and then goes on to say:
All of these problems translate into failures in the boost::function constructors or assignment operator, even if the user never invokes operator==. We can't do that to users.
Update
Found a standards rationale in Accessing the target of a tr1::function object, which is pretty old but is consistent with the boost FAQ and says:
operator== is unimplementable for tr1::function within the C++ language, because we do not have a reliable way to detect if a given type T is Equality Comparable without user assistance.
Why is std::function not equality comparable?
Why is
std::function
not equality comparable?
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.
What is the "possible hole in the type system?"
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.
How is it different from
std::shared_ptr
?
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.
std::function and comparison of std::function
A std::function
is not a function pointer. You cannot compare std::function
s like you compare function pointers. If you comparing two std::function
s, and expecting them to compare equal to each if they've type-erased the same function, that's not going to work. A std::function
can only be compared to a nullptr
. This is the only defined equality operator for std::function
s.
Your goal is to identify a std::function
and remove it from a vector of installed callback functions. The usual way this kind of functionality is done is by assigning a separate label to a wrapped function, often a std::string
that gives a unique name to each function, and then you find the function by name. It doesn't have to be a std::string
, an enum
will work just as well. The bottom line is that you will have to establish how your std::function
s are identified "out of band".
Check if two std::function are Equal
I dont think that they both can be compared. Here is an example to explain some points on std::function comparison
Can two functors be compared for equality?
A raw function is eventually a pointer. You can dig it out of std::function
with std::function::target
and then it's simply a comparison of void*
.
How to compare two vectors for equality?
You can construct a std::unordered_set
from each vector, then compare those, as shown in the code snippet below:
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int main()
{
std::vector<int> nums = { 1, 2, 3, 4, 5 };
std::vector<int> nums2 = { 5, 4, 3, 2, 1 };
std::vector<int> nums3 = { 5, 4, 9, 2, 1 };
std::unordered_set<int> s1(nums.begin(), nums.end());
std::unordered_set<int> s2(nums2.begin(), nums2.end());
std::unordered_set<int> s3(nums3.begin(), nums3.end());
if (s1 == s2) {
std::cout << "1 and 2 are equal";
}
else {
std::cout << "1 and 2 are different";
}
std::cout << std::endl;
if (s1 == s3) {
std::cout << "1 and 3 are equal";
}
else {
std::cout << "1 and 3 are different";
}
std::cout << std::endl;
return 0;
}
However, there are some points to bear in mind:
- For vectors of custom type objects, you would need to provide an
operator==
for that type (but that would have to be done anyway, or how can you say if the two vectors have the same contents). - Vectors that containing duplicate entries will create sets that have those duplicates removed: Thus,
{1, 2, 2, 3}
will show equal to{1, 2, 3}
. - You will also need to provide a
std:hash
for your custom type. For a trivial class,bob
, which just wraps an integer, that hash, and the requiredoperator==
, could be defined as shown below; you can then replace the<int>
specializations in the above example with<bob>
and it will work. (This cppreference article explains more about the hash.)
class bob {
public:
int data;
bob(int arg) : data{ arg } { }
};
bool operator==(const bob& lhs, const bob& rhs)
{
return lhs.data == rhs.data;
}
template<> struct std::hash<bob> {
std::size_t operator()(bob const& b) const noexcept {
return static_cast<size_t>(b.data);
}
};
Comparing std::function
Based on information from Stack Overflow at the following link, it IS possible but only if you wrap the std::function object in its own class.
std::vector of std::function
By using a wrapper class, you can test whether two wrapped std::function pointers are equal, but that doesn't tell you anything about what the std::function wraps. So, changing your design is probably a better approach.
edit: I came back to show the way I've solved a very similar problem.
0) Typedefs for conciseness.
using std::placeholders;
typedef std::function < void ( int, float ) > some_func;
typedef std::pair < intptr_t, intptr_t > method_hash;
Write your collection of std::function objects by binding pointers to methods or functions. Where you are doing this for static functions, omit some_object_ptr.
some_func some_method ( std::bind ( some_method_ptr,
some_object_ptr, _1, _2 )Use std::reinterpret_cast < intptr_t > to create a unique hash for your function, and use it with std::pair to do so for methods.
method_hash pairID ( reinterpret_cast < intptr_t > ( some_object_ptr ),
reinterpret_cast < intptr_t > ( some_method_ptr ) );Now your pairID can be stored in a vector or other container/array. Just be sure to maintain that the indices are aligned so that a hash always corresponds to the correct std::function object, and you can then use find ( ) to get an iterator to its position and distance ( ) to convert the iterator to the required index.
Note that this will have to be done every time your container is generated. Since it's based on pointers, the hashes will change over different runs of your program.
How can I compare std::function objects?
As shown here, template member function target
accepts type that is compared with a stored object type. In you case it is a pointer to a function. You have to change
*iter->target<void()>() == myFunc2
to
*iter->target<void(*)()>() == myFunc2
Note that this will only only you to find a plain C function, not an arbitrary callable object (like a lambda function). I think you should consider using plain pointers instead of std::function
here.
Related Topics
Template Parameter Packs Access Nth Type and Nth Element
List of C++ Name Resolution (And Overloading) Rules
C++ Ifstream Error Using String as Opening File Path
How to Merge Two Bst's Efficiently
Current Date and Time as String
Clang: No Out-Of-Line Virtual Method Definitions (Pure Abstract C++ Class)
Add External Libraries to Cmakelist.Txt C++
How to Create an Array of Pointers
Image Edge Smoothing with Opencv
Do C++11 Lambdas Capture Variables They Don't Use
Std::Vector Reserve() and Push_Back() Is Faster Than Resize() and Array Index, Why
Are There Binary Memory Streams in C++
Using Declaration in Variadic Template
What Does an Ampersand After This Assignment Operator Mean
Align Cout Format as Table's Columns
Are C++ Libs Created with Different Versions of Visual Studio Compatible with Each Other