Using generic std::function objects with member functions in one class
Solution 1
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.)
Solution 2
Either you need
std::function<void(Foo*)> f = &Foo::doSomething;
so that you can call it on any instance, or you need to bind a specific instance, for example this
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
Solution 3
If you need to store a member function without the class instance, you can do something like this:
class MyClass
{
public:
void MemberFunc(int value)
{
//do something
}
};
// Store member function binding
auto callable = std::mem_fn(&MyClass::MemberFunc);
// Call with late supplied 'this'
MyClass myInst;
callable(&myInst, 123);
What would the storage type look like without auto? Something like this:
std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable
You can also pass this function storage to a standard function binding
std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call
Past and future notes: An older interface std::mem_func existed, but has since been deprecated. A proposal exists, post C++17, to make pointer to member functions callable. This would be most welcome.
Solution 4
Unfortunately, C++ does not allow you to directly get a callable object referring to an object and one of its member functions. &Foo::doSomething
gives you a "pointer to member function" which refers to the member function but not the associated object.
There are two ways around this, one is to use std::bind
to bind the "pointer to member function" to the this
pointer. The other is to use a lambda that captures the this
pointer and calls the member function.
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};
I would prefer the latter.
With g++ at least binding a member function to this will result in an object three-pointers in size, assigning this to an std::function
will result in dynamic memory allocation.
On the other hand, a lambda that captures this
is only one pointer in size, assigning it to an std::function
will not result in dynamic memory allocation with g++.
While I have not verified this with other compilers, I suspect similar results will be found there.
Solution 5
You can avoid std::bind
doing this:
std::function<void(void)> f = [this]-> {Foo::doSomething();}
Related videos on Youtube
Christian Ivicevic
Updated on July 08, 2022Comments
-
Christian Ivicevic almost 2 years
For one class I want to store some function pointers to member functions of the same class in one
map
storingstd::function
objects. But I fail right at the beginning with this code:#include <functional> class Foo { public: void doSomething() {} void bindFunction() { // ERROR std::function<void(void)> f = &Foo::doSomething; } };
I receive
error C2064: term does not evaluate to a function taking 0 arguments
inxxcallobj
combined with some weird template instantiation errors. Currently I am working on Windows 8 with Visual Studio 2010/2011 and on Win 7 with VS10 it fails too. The error must be based on some weird C++ rules i do not follow -
Christian Ivicevic over 12 yearsAs i am not dependant on boost i will use lambda expressions ;) Nevertheless thanks!
-
ildjarn over 12 years@AlexB : Boost.Bind doesn't use ADL for the placeholders, it puts them in an anonymous namespace.
-
Max Raskin over 7 yearsI recommend to avoid global capture [=] and use [this] to make it clearer what is captured (Scott Meyers - Effective Modern C++ Chapter 6. item 31 - Avoid default capture modes)
-
Max Truxa over 7 years@Danh
std::mem_fn
was not removed; a bunch of unnecessary overloads were. On the other handstd::mem_fun
was deprecated with C++11 and will be removed with C++17. -
Max Truxa over 7 years@Danh That's exactly what I'm talking about ;) The very first "basic" overload is still there:
template<class R, class T> unspecified mem_fn(R T::*);
, and it won't go away. -
Max Truxa over 7 years@Danh Read the DR carefully. 12 out of 13 overloads were removed by the DR. That last one was not (and won't be; neither in C++11 or C++14).
-
Greg over 7 yearsWhy the down vote? Every other response said you must bind the class instance. If you are creating a binding system for reflection or scripting, you will not want to do that. This alternative method is valid and relevant for some people.
-
Greg over 7 yearsThanks Danh, I've edited with some comments about related past and future interfaces.
-
penelope about 6 yearsThank you for this great answer :D Exactly what I need, I couldn't find how to specialize a std::function to call a member function on any class instance.
-
sudo rm -rf slash almost 5 yearsThis compiles, but is it standard? Are you guaranteed that the first argument is
this
? -
Armen Tsirunyan almost 5 years@sudorm-rfslash yes, you are
-
sudo rm -rf slash almost 5 yearsThanks for the reply @ArmenTsirunyan ... where in the standard can I look for this info?
-
landerlyoung over 4 yearsJust add a little tip: member function pointer can be implicitly cast to
std::function
, with extrathis
as it's first parameter, likestd::function<void(Foo*, int, int)> = &Foo::doSomethingArgs
-
Val almost 4 years@landerlyoung: Add the name of the function say as "f" above to fix the sample syntax. If you don't need the name you could use mem_fn(&Foo::doSomethingArgs).
-
Minwoo Yu over 2 yearsTo do this, ``` #include<functional>``` need.