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

185,592

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();}
Share:
185,592

Related videos on Youtube

Christian Ivicevic
Author by

Christian Ivicevic

Updated on July 08, 2022

Comments

  • Christian Ivicevic
    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 storing std::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 in xxcallobj 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
    Christian Ivicevic over 12 years
    As i am not dependant on boost i will use lambda expressions ;) Nevertheless thanks!
  • ildjarn
    ildjarn over 12 years
    @AlexB : Boost.Bind doesn't use ADL for the placeholders, it puts them in an anonymous namespace.
  • Max Raskin
    Max Raskin over 7 years
    I 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
    Max Truxa over 7 years
    @Danh std::mem_fn was not removed; a bunch of unnecessary overloads were. On the other hand std::mem_fun was deprecated with C++11 and will be removed with C++17.
  • Max Truxa
    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
    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
    Greg over 7 years
    Why 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
    Greg over 7 years
    Thanks Danh, I've edited with some comments about related past and future interfaces.
  • penelope
    penelope about 6 years
    Thank 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
    sudo rm -rf slash almost 5 years
    This compiles, but is it standard? Are you guaranteed that the first argument is this?
  • Armen Tsirunyan
    Armen Tsirunyan almost 5 years
    @sudorm-rfslash yes, you are
  • sudo rm -rf slash
    sudo rm -rf slash almost 5 years
    Thanks for the reply @ArmenTsirunyan ... where in the standard can I look for this info?
  • landerlyoung
    landerlyoung over 4 years
    Just add a little tip: member function pointer can be implicitly cast to std::function, with extra this as it's first parameter, like std::function<void(Foo*, int, int)> = &Foo::doSomethingArgs
  • Val
    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
    Minwoo Yu over 2 years
    To do this, ``` #include<functional>``` need.