33

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);
}

5 Answers5

29

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • How is it correct? I think `std::function` is capable of storing a free function of this type : `typedef int (*FunType)(Type&)`, that is, a function which takes an argument of type `Type&` and returns `int`. – Nawaz Feb 14 '12 at 17:59
  • 3
    @Nawaz: Have you tried it in the compiler? [It works.](http://ideone.com/LKSZD) The spec says that it works. I can't really cite the spec, because the definition of this stuff is scattered across about 4 sections of the section 20.8, but pay close attention to 20.8.2 and 20.8.11. – Nicol Bolas Feb 14 '12 at 18:03
  • 1
    I'm completely surprised. :|. +1 – Nawaz Feb 14 '12 at 18:09
  • It appears correct so i'm accepting but `typedef int (A::*MemFn)(double d);` still looks easier ;). I wasn't very concerned about the object, just how to specify the function pointer –  Feb 14 '12 at 18:18
  • How do you know that the instance pointer is the *first* argument? Or that it's even a pointer? All those are implementation details I believe. What is correct is that `bind` takes as its second argument an instance pointer or reference if the first argument is a PTMF and produces The Right Thing. – Kerrek SB Feb 14 '12 at 18:20
  • Btw, the code i was writing has a list of A*'s and it calls MemFn on each of them. I'm just saying this fact so my last comment is more clear –  Feb 14 '12 at 18:20
  • 1
    See section [func.require] for the definition of 'callable' used by `std::fuction`. `std::function` absolutely can store and call a pointer-to-member-function. The first argument to the `std::function` object is an object of the appropriate type or a pointer to such an object. – bames53 Feb 14 '12 at 18:24
  • @KerrekSB: Because the C++11 specification *says so*. If the function argument it is given is a member pointer (data or function), then the next argument had better be an object of that type, reference of that type, or something that `(*t1).*f` is legal syntax for. Otherwise an exception will be thrown for a bad call. – Nicol Bolas Feb 14 '12 at 18:24
  • @KerrekSB the instance pointer is the first argument because that's what 20.8.2 says: `Define INVOKE(f, t1, t2, ..., tN) as follows: [...] (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T; [...]` – bames53 Feb 14 '12 at 18:27
  • @Nicol: I think we're talking about different things. I know that you can INVOKE and whatnot. Rather, I am contesting your claim in the post that "the first argument of a member function is always an object pointer". I don't think that's specified. In `struct Foo { void bar(); };`, the member function `Foo::bar` doesn't have any explicit arguments, and the implicit instance argument isn't ordered with respect to the other arguments in any prescribed sense. All you can do is invoke it via the `->*` or `.*` syntax. – Kerrek SB Feb 14 '12 at 18:33
  • @KerrekSB: Fair enough. Corrected. – Nicol Bolas Feb 14 '12 at 18:44
10

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Reading the spec (func.bind.bind) I think `std::bind` is different from `boost::bind` in that it produces a callable object that takes exactly the number of arguments specified, requiring the use of placeholders. So you'd need to say `bind(&Foo::bar,x,_1,_2)` and I think calling with a different number of arguments is undefined. Simulated variadic templates probably can't implement this. – bames53 Feb 14 '12 at 21:53
  • @bames53: You're right, the additional parameters should probably be there. I had some strange feeling that they'd be added implicitly, but that may well have referred to the boost version. Anyway, let me edit that. – Kerrek SB Feb 14 '12 at 22:12
  • Nice solution, but it should be `using namespace std::placeholders;` – Gene Vincent Jul 12 '15 at 12:25
  • @GeneVincent: Thanks, fixed! – Kerrek SB Jul 12 '15 at 12:28
7

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).

davidhigh
  • 14,652
  • 2
  • 44
  • 75
0

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;
}
Bojan Komazec
  • 9,216
  • 2
  • 41
  • 51
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.

Mark Ingram
  • 71,849
  • 51
  • 176
  • 230