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)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;—
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;—
t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;—
(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
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, andTest::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
Passing Shared Pointers as Arguments
Is It a Good Practice to Define C++ Functions Inside Header Files
How to Create a Utf-8 String Literal in Visual C++ 2008
How to Write C++ Getters and Setters
Allocconsole() Not Displaying Cout
How to Create the Cartesian Product of a Type List
How to Return a Char Array from a Function
What Does a Backslash in C++ Mean
Retrieving File Descriptor from a Std::Fstream
Why Is the Empty Base Class Optimization (Ebo) Is Not Working in Msvc
Visual Studio Code Formatting for "{ }"
Return a "Null" Object If Search Result Not Found
How to Get the Real and Total Length of Char * (Char Array)
Default Argument in the Middle of Parameter List
How to Get Function Name Inside a C++ Function
Cancelling Boost Asio Deadline Timer Safely
Preincrement Faster Than Postincrement in C++ - True? If Yes, Why Is It