Comparing std::function for member functions

2019-07-07 04:54发布

问题:

I tried to search and here similar questions:
question 1
question 2

But anyway, I can't compare member functions. Here's an example:

class ClassA
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
};

int main()
{
    ClassA a1{};

    function<int(int, int)> f1 = bind(&ClassA::add, a1, placeholders::_1, placeholders::_2);
    function<int(int, int)> f2 = bind(&ClassA::add, a1, placeholders::_1, placeholders::_2);

    cout << boolalpha << "f1 == f2 " << (f1.target_type() == f2.target_type()) << endl; // true
    cout << (f1.target<int(ClassA::*)(int, int)>() == nullptr) << endl; // true

    return 0;
}

From the code it's obvious that f1 and f2 are different. The first cout shows true because the types are the same, it's ok. But why the second cout is true? Why function::target() returns nullptr?

P.S.: I want to create a simple delegate system so I can pass any function (global, static, member) around. With std::function I can add a callback, but I don't know how to remove it.

回答1:

That's because f1's target type is not int(ClassA::*)(int, int). Its target type is going to be the result of that bind expression, which on gcc happens to be:

std::_Bind<std::_Mem_fn<int (ClassA::*)(int, int)> (
    ClassA, 
    std::_Placeholder<1>, 
    std::_Placeholder<2>)>

Which you can see using the ABI demangler:

#include <cxxabi.h>
// note, the following line technically leaks, but 
// for illustrative purposes only it's fine
cout << abi::__cxa_demangle(f1.target_type().name(), 0, 0, 0) << endl;

Note that if the target type was actually a class method, you wouldn't be able to call it with two ints - you'd also need the ClassA*. For instance, this function's target type is int(ClassA::*)(int, int):

function<int(ClassA*, int, int)> f3 = &ClassA::add;


回答2:

Those std::functions do not hold member functions. They hold bind result types. As the bind is the same pattern of types, the bind result types are the same.