Using Std::Bind with Member Function, Use Object Pointer or Not for This Argument

Using std::bind with member function, use object pointer or not for this argument?

Both are correct. 20.8.9.1.2 forwards to 20.8.2 to describe the requirements and the effect of your call to bind. 20.8.2 is:

20.8.2 Requirements [func.require]

1 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;

((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;

t1.*f when N == 1 and f is a pointer to member data 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;

(*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;

f(t1, t2, ..., tN) in all other cases.

The first two options allow both a reference and a pointer.

The important thing to notice here is that the wording does not limit you to plain pointers. You could use a std::shared_ptr or some other smart pointer to keep your instance alive while bound and it would still work with std::bind as t1 is dereferenced, no matter what it is (given, of course, that it's possible).

How std::bind works with member functions

When you say "the first argument is a reference" you surely meant to say "the first argument is a pointer": the & operator takes the address of an object, yielding a pointer.

Before answering this question, let's briefly step back and look at your first use of std::bind() when you use

std::bind(my_divide, 2, 2)

you provide a function. When a function is passed anywhere it decays into a pointer. The above expression is equivalent to this one, explicitly taking the address

std::bind(&my_divide, 2, 2)

The first argument to std::bind() is an object identifying how to call a function. In the above case it is a pointer to function with type double(*)(double, double). Any other callable object with a suitable function call operator would do, too.

Since member functions are quite common, std::bind() provides support for dealing with pointer to member functions. When you use &print_sum you just get a pointer to a member function, i.e., an entity of type void (Foo::*)(int, int). While function names implicitly decay to pointers to functions, i.e., the & can be omitted, the same is not true for member functions (or data members, for that matter): to get a pointer to a member function it is necessary to use the &.

Note that a pointer to member is specific to a class but it can be used with any object that class. That is, it is independent of any particular object. C++ doesn't have a direct way to get a member function directly bound to an object (I think in C# you can obtain functions directly bound to an object by using an object with an applied member name; however, it is 10+ years since I last programmed a bit of C#).

Internally, std::bind() detects that a pointer to a member function is passed and most likely turns it into a callable objects, e.g., by use std::mem_fn() with its first argument. Since a non-static member function needs an object, the first argument to the resolution callable object is either a reference or a [smart] pointer to an object of the appropriate class.

To use a pointer to member function an object is needed. When using a pointer to member with std::bind() the second argument to std::bind() correspondingly needs to specify when the object is coming from. In your example

std::bind(&Foo::print_sum, &foo, 95, _1)

the resulting callable object uses &foo, i.e., a pointer to foo (of type Foo*) as the object. std::bind() is smart enough to use anything which looks like a pointer, anything convertible to a reference of the appropriate type (like std::reference_wrapper<Foo>), or a [copy] of an object as the object when the first argument is a pointer to member.

I suspect, you have never seen a pointer to member - otherwise it would be quite clear. Here is a simple example:

#include <iostream>

struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};

void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}

int main() {
Foo foo1{1};
Foo foo2{2};

apply(&foo1, &foo2, &Foo::f);
apply(&foo1, &foo2, &Foo::g);
}

The function apply() simply gets two pointers to Foo objects and a pointer to a member function. It calls the member function pointed to with each of the objects. This funny ->* operator is applying a pointer to a member to a pointer to an object. There is also a .* operator which applies a pointer to a member to an object (or, as they behave just like objects, a reference to an object). Since a pointer to a member function needs an object, it is necessary to use this operator which asks for an object. Internally, std::bind() arranges the same to happen.

When apply() is called with the two pointers and &Foo::f it behaves exactly the same as if the member f() would be called on the respective objects. Likewise when calling apply() with the two pointers and &Foo::g it behaves exactly the same as if the member g() would be called on the respective objects (the semantic behavior is the same but the compiler is likely to have a much harder time inlining functions and typically fails doing so when pointers to members are involved).

std::bind with member function and this in c++

Let the compiler deduce types based on passed arguments:

std::function<void(void)> function2 = std::bind(&Class2::print, this);

above is enough.

Version with explicit template arguments list looks like:

std::function<void(void)> function3 = std::bind<void(Class2::*)(),Class2*>(&Class2::print, this);

// std::bind<void(Class2::*)(),Class2*>
/ \ / \
| |---- pointer to type of object instance you invoke member function
|--- pointer to member function

Demo

C++ bind member functions using pointers

_1 ... _N are placeholders (which is pretty obvious as they are in std::placeholders.
What you do by using them is to tell which argument to use for which function parameter.

SomeClasss obj;
auto f1 = std::bind(&SomeClass::SomeFunction, &obj, _1, _2);

This means the first argument of f1 will be used as first parameter when SomeClass::SomeFunction is called and the second argument of f1 will be used as second paramater.
You could also do somthing like

SomeClass obj;
auto f2 = std::bind(&SomeClass::SomeFunction, &obj, _2, _1);

Now the first argument of f2 is the second paramter and the second argument is the first parameter when SomeClass::SomeFunction is called.

So what you do with

EvilBadGuy ebg3(std::bind(&GameLevel::health, std::cref(currentLevel),_1))

is to contruct and EvilBadGuy object with its healthFunc beeing GameLevel::health on the object currentLevel. The first argument of healthFunc will be the first parameter passed to the GameLevel::health.

lambda iso std::bind for member function

You can use lambda's capture list to capture member function pointer and object pointer and invoke them inside the lambda. Try this:

#include <functional>

template<typename Foo>
class Bar
{
public:

template<class THandlerObj>
Bar(void (THandlerObj::*pCmdHandler)(const Foo&),
THandlerObj* pCmdHandlerContext)
: m_cmdHandlerFunc(
[=](const Foo& foo) { (pCmdHandlerContext->*pCmdHandler)(foo); })
{
}

private:
std::function<void(const Foo&)> m_cmdHandlerFunc;
};

If your compiler supports C++20, you can also use std::bind_front which is more lightweight and intuitive than std::bind.

template<class THandlerObj>
Bar(void (THandlerObj::*pCmdHandler)(const Foo&),
THandlerObj* pCmdHandlerContext)
: m_cmdHandlerFunc(std::bind_front(pCmdHandler, pCmdHandlerContext))
{
}

Can I use std::bind to convert a pointer to member function into a pointer to function?

NathanOliver's comment is correct, and your suspicion is mostly correct. Exactly how pointers to member functions work is not specified, but including this as a hidden argument mostly works. You just need a bit of extra work for inheritance and pointers to virtual functions (yes, you can take their address too).

Now, often callbacks include a void* parameter under your control, which you can use to pass a A*. In those cases, you can write a wrapper (static) function that casts the void* back to A* and does the actual call to &A::callback.

That's not the case here. Registration takes a single function, without data. To get this to work in real-life situations, you have to resort to drastic solutions - not portable C++. One such method is to dynamically generate assembly (!). You create - at runtime - the compiled equivalent of

void __trampoline_0x018810000 (int i)
{
A* __this = reinterpret_cast<A*>(0x018810000);
__this->callback(i);
}

As you can see, you have to generate one trampoline for every A* value, and managing lifetimes of these is a major pain.

Why can std::function bind functions of different types?

func_type has two parameters, and Test::state_function only has one parameter,I can't understand how it works.

For binding purposes, the non-static member function state_function has an additional first implicit object parameter of type const Test&.

Now, when we define a std::function object, we specify the function type that is the signature of the callable object that object can represent. When the callable is a member function, the signature’s first parameter represent the (normally implicit) object on which the member function will be called on. Now, there are two ways to specify the signature in case of a member function:

Method 1

Here we say that the object on which the member function will be called will be passed as a pointer.

//----------------------------------vvvvv-------------------------->pointer here means a pointer to the object of type `Test` will be passed
using func_type = std::function<int(Test* const, const double)>;

This method 1 is what you used in your example. It means that the member function was being called using a pointer to an object of type Test.

Method 2

But there is another way of passing the object. In particular, you can sepcify that the object will be passed as a reference as shown below:

//----------------------------------------vvvvv------------------>note the reference here which says that here an object of type `Test` will be passed instead of a pointer 
using func_type = std::function<int(const Test&, const double)>;

Note also that for the above to work you will have to modify it->second(this, 0.0); to

//---------vvvvv-------------->note the * used here to dereference 
it->second(*this, 0.0);

Demo

Using generic std::function objects with member functions in one class

A non-static member function must be called with an object. That is, it always implicitly passes "this" pointer as its argument.

Because your std::function signature specifies that your function doesn't take any arguments (<void(void)>), you must bind the first (and the only) argument.

std::function<void(void)> f = std::bind(&Foo::doSomething, this);

If you want to bind a function with parameters, you need to specify placeholders:

using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, std::placeholders::_1, std::placeholders::_2);

Or, if your compiler supports C++11 lambdas:

std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}

(I don't have a C++11 capable compiler at hand right now, so I can't check this one.)

std::bind of class member function

std::bind() accepts its arguments by value. This means that in the first case you are passing a pointer by value, resulting in the copy of a pointer. In the second case, you are passing an object of type foo by value, resulting in a copy of an object of type Foo.

As a consequence, in the second case the evaluation of the expression L() causes the member function get() to be invoked on a copy of the original object foo, which may or may not be what you want.

This example illustrates the difference (forget the violation of the Rule of Three/Rule of Five, this is just for illustration purposes):

#include <iostream>
#include <functional>

struct Foo
{
int _x;

Foo(int x) : _x(x) { }

Foo(Foo const& f) : _x(f._x)
{
std::cout << "Foo(Foo const&)" << std::endl;
}

int get(int n) { return _x + n; }
};

int main()
{
Foo foo1(42);

std::cout << "=== FIRST CALL ===" << std::endl;
auto L1 = std::bind(&Foo::get, foo1, 3);
foo1._x = 1729;
std::cout << L1() << std::endl; // Prints 45

Foo foo2(42);

std::cout << "=== SECOND CALL ===" << std::endl;
auto L2 = std::bind(&Foo::get, &foo2, 3);
foo2._x = 1729;
std::cout << L2() << std::endl; // Prints 1732
}

Live example.

If, for any reason, you don't want to use the pointer form, you can use std::ref() to prevent a copy of the argument from being created:

auto L = std::bind(&Foo::get, std::ref(foo), 3);


Related Topics



Leave a reply



Submit