27

I can easily bind member functions to a std::function by wrapping them with a lambda expression with capture clause.

class Class
{
    Class()
    {
        Register([=](int n){ Function(n); });
    }

    void Register(std::function<void(int)> Callback)
    {

    }

    void Function(int Number)
    {

    }
};

But I want to bind them directly, something like the following.

// ...
Register(&Class::Function);
// ...

I think according to the C++11 standard, this should be supported. However, in Visual Studio 11 I get these compiler errors.

error C2440: 'newline' : cannot convert from 'int' to 'Class *'

error C2647: '.*' : cannot dereference a 'void (__thiscall Class::* )(int)' on a 'int'

danijar
  • 27,743
  • 34
  • 143
  • 257

5 Answers5

47

I think according to the C++11 standard, this should be supported

Not really, because a non-static member function has an implicit first parameter of type (cv-qualified) YourType*, so in this case it does not match void(int). Hence the need for std::bind:

Register(std::bind(&Class::Function, PointerToSomeInstanceOfClass, _1));

For example

Class c;
using namespace std::placeholders; // for _1, _2 etc.
c.Register(std::bind(&Class::Function, &c, _1));

Edit You mention that this is to be called with the same Class instance. In that case, you can use a simple non-member function:

void foo(int n)
{
  theClassInstance.Function(n);
}

then

Class c;
c.Register(foo);
juanchopanza
  • 210,243
  • 27
  • 363
  • 452
  • Works, but can the syntax be simplified under the constraint that all the class reference are the same? – danijar Jun 16 '13 at 09:24
  • @danijar what do you mean by all the class references being the same? You need to pass a pointer to a `Class` instance. Which one you pass is up to you. – juanchopanza Jun 16 '13 at 09:26
  • 1
    Right. And this instance will always be the same. Now I would like to shorten the `bind(&Class::Function, this, _1)` with this information in mind. So, contentually, the `bind` is obsolete. Is there a technical way to get rid of it or perform the binding inside the function after passing? – danijar Jun 16 '13 at 09:31
  • @danijar will it be some kind of global instance or singleton? If so, then you can also make a global `std::function` where you bind to that instance. Then you just re-use that. Actually, there is no need to keep a global `std::function`. A simple non-member function could do the trick. – juanchopanza Jun 16 '13 at 09:33
  • No, sadly it isn't a global instance. – danijar Jun 16 '13 at 10:15
  • @danijar so what is it? Maybe you can post some code to clarify this? – juanchopanza Jun 16 '13 at 10:16
  • It is just a self containing class with different use cases. But if there is no way, I am going to accept that. – danijar Jun 16 '13 at 10:22
  • @juanchopanza, what does `void (int n) foo` mean? – Sasha Oct 28 '19 at 20:04
  • 1
    @Sasha Probably `void foo(int n)`. Fixed. – juanchopanza Oct 28 '19 at 21:54
36

According to Stephan T. Lavavej - "Avoid using bind(), ..., use lambdas". https://www.youtube.com/watch?v=zt7ThwVfap0&t=32m20s

In this case:

Class()
{
    Register([this](int n){ Function(n); });
}
Andy
  • 876
  • 11
  • 14
9

You can use std::bind:

using namespace std::placeholders;  // For _1 in the bind call

// ...

Register(std::bind(&Class::Function, this, _1));
Some programmer dude
  • 363,249
  • 31
  • 351
  • 550
  • Say, I want to set this function pointer as default argument for the function. Is this possible? I can't use calls like `bind` then, do I? I haven't the `this` pointer to find in the parameter list. – danijar Jun 16 '13 at 09:28
  • 1
    @danijar That's not possible, but can be worked around by having an overloaded variant of `Register` with no arguments that binds to your default function. – Some programmer dude Jun 16 '13 at 09:32
  • I was missing the std::placeholders in my code and this answer pointed me to it! – Martin Meeser Feb 08 '16 at 08:45
3

In C++ 17, you can use:

Register([=](auto && ...args){ return Function(args...); });

which is sweet especially if the argument list is longer long. Of course the member function's argument list must then be compatible with the std::function's ones.

peterchen
  • 38,919
  • 19
  • 95
  • 176
2

With std::function and std::bind, you can treat different class member function the same.

#include <iostream>
#include <functional>
#include <vector>
using namespace std;
using namespace std::placeholders;

class Foo
{
public:
    void foo(const string &msg)
    {
        cout << msg << '\n';
    }
};

class Bar
{
public:
    void bar(const string &msg, const string &suffix)
    {
        cout << msg << suffix << '\n';
    }
};

int main(int argc, char **argv)
{
    Foo foo;
    Bar bar;

    vector<function<void (const string &msg)>> collection;
    collection.push_back(bind(&Foo::foo, &foo, _1));
    collection.push_back(bind(&Bar::bar, &bar, _1, "bar"));

    for (auto f : collection) {
        f("foo");
    }

    return 0;
}
Richard
  • 41
  • 1