37

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'

asked Jun 16, 2013 at 9:17

6 Answers 6

63

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);
answered Jun 16, 2013 at 9:20
Sign up to request clarification or add additional context in comments.

9 Comments

Works, but can the syntax be simplified under the constraint that all the class reference are the same?
@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.
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 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.
@Sasha Probably void foo(int n). Fixed.
|
46

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); });
}
answered Apr 13, 2016 at 10:58

8 Comments

thanks for pointing this out and linking to the relevant portion of the video!
This is arguably cleaner syntactically, but you've effectively nested a function inside a function rather than allowing it to be invoked directly, so each call is ever so slightly less efficient and slower. You've also used an allocating method to set it up, which burns just a bit more memory and CPU time on that step. The std::bind is ugly but more direct and efficient I believe.
@BuvinJ Believe is not necessarily best engineering judgement, e.g.: godbolt.org/z/doaKrWWEb vs godbolt.org/z/jf7aoWa4P
@Andy Are you basically saying that some compilers optimize the use of a lamba which is written as a wrapper like this? But not, std::bind?
Note that another option would be to just use an actual function pointer to a class member, rather then an std::function at all... I realize this is not quite as flexible but might be the MOST efficient solution?
|
10

You can use std::bind:

using namespace std::placeholders; // For _1 in the bind call
// ...
Register(std::bind(&Class::Function, this, _1));
answered Jun 16, 2013 at 9:20

3 Comments

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 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.
I was missing the std::placeholders in my code and this answer pointed me to it!
8

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.

answered Jul 19, 2019 at 13:54

2 Comments

And the example code no longer works in C++20, due to the implicit capture of this via [=] being deprecated.
isn't std::forward needed there to make perfect forwarding?
3

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;
}
answered Aug 13, 2013 at 11:29

Comments

0

As others have already stated, you can use std::bind. The syntax is pretty ugly though. As such, I offer these macros:

#define bindToThis( className, funcName ) \
 std::bind( &className::funcName, this, std::placeholders::_1 )
#define bindToThat( className, funcName, objPtr ) \
 std::bind( &className::funcName, objPtr, std::placeholders::_1 )

Here's an example implementation of the macro, after first typedefing the std::function to define the required signature (which makes that easier to read/use as well):

typedef std::function<void(const int)> MyIntHandler;
class MyClass 
{
public:
 MyClass( const MyIntHandler handler );
};
...
SomeOtherClass::SomeOtherClass() 
 : myObjPtr_( new MyClass( 
 MyIntHandler( bindToThis( SomeOtherClass, handleSomeInt ) ) )
{}
void SomeOtherClass::handleSomeInt( const int i )
{
 // Do something...
}
answered Jun 9, 2023 at 13:35

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.