简体   繁体   中英

How do i write a pointer-to-member-function with std::function?

I know how to declare int fn(double) inside of std::function ( std::function<int(double)> ). I know how to write a pointer-to-member-function ( typedef int (A::*MemFn)(double d); ). But how do i write a pointer-to-member-function with std::function?

Dummy code if you feel like compiling/testing

-edit- based on answers i think i'll just use the typedef and not bother with std::function

#include <cstdio>
#include <functional>

struct A{ int fn(double){ return 0; } };
int fn2(double){ return 0; }

typedef int (A::*MemFn)(double d);
typedef std::function<int(double)> MemFn2;

void Test(A*a, MemFn2 fn){
    fn(1.2f);
}
void Test(A*a, MemFn fn){
    (a->*fn)(1.2f);
}

int main(){
    Test(new A, &A::fn);
    Test(new A, &fn2);
}

std::function is perfectly capable of storing a member function pointer directly. However, you have to adjust the argument list appropriately. Member pointers must be called with an instance of the type (or a derived type). When putting them in a std::function , the first argument in the argument list is expected to be a pointer (or reference or smart-pointer) to the object type.

So, if I have the following class:

struct Type
{
public:
    int Foo();
};

The correct syntax to store this member function in a std::function is:

std::function<int(Type&)> fooCaller = &Type::Foo;

If you want to preserve the argument list (in your case, int(double) ), then you need to provide the instance outside of the function . This can be done via std::bind :

struct A{ int fn(double){ return 0; } };

A anInstance;
std::function<int(double)> fnCaller = std::bind(&A::fn, &anInstance, std::placeholders::_1);

Note that it is your responsibility to ensure that the object pointer you provide to std::bind remains alive so long as fnCaller is alive. If you return fnCaller to someone, and it has a pointer to a stack object, you're in trouble.

What's nice is that you could bind a shared_ptr (or any copyable smart pointer) as your object, thanks to how the function call mechanism is defined:

struct A{ int fn(double){ return 0; } };

auto anInstance = std::make_shared<A>();
std::function<int(double)> fnCaller = std::bind(&A::fn, anInstance, std::placeholders::_1);

Now you don't have to worry; the binder will continue to keep the object alive, since it stores a shared_ptr by value.

A member function is not a function. It is not itself anything you can call. All you can do is call a member function of an instance object . Only the pair of pointer-to-member-function and object constitutes a callable entity.

To bind an instance to a PTMF and obtain something callable, use bind :

#include <functional>

struct Foo
{
    double bar(bool, char);
};

Foo x;
using namespace std::placeholders;
std::function<double(bool, char)> f = std::bind(&Foo::bar, x, _1, _2);
f(true, 'a'); //...

As with lambdas, bind expressions have an unknowable type, and the conversion to std::function (as well as the actual dispatch) is potentially expensive. If possible, it is preferable to use auto for the type of the bind expression.

One of the guidelines in Scott Meyer's Modern C++11 book is to avoid std::bind and always use a lambda closure instead:

struct A{ int fn(double){ return 0; } };

std::function<int(double)> f = [a = A{}](double x) mutable { return a.fn(x); };

The mutable is necessary here, as the capture a might potentially be changed by the function call (since A::fn is non-const).

You can use std::binder1st to bind member function to a class instance:

typedef std::binder1st<std::mem_fun1_t<int, A, double>> MemFn;

void Test(A* a, double d)
{
   MemFn fn(std::mem_fun(&A::fn), a);
   int nRetVal = fn(d);
}

int main()
{
   Test(new A, 1.2f);
   return 0;
}

If you can use Boost then you can use Boost.Bind . It's easily accomplished like this:

boost::bind(&MyClass::MemberFunction, pInstance, _1, _2)

Hopefully it's fairly self-explanatory. _1 and _2 are placeholders for parameters you can pass through to the function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM