11

I am trying to learn std::function and here's my code:

#include <iostream>
#include <functional>

struct Foo {
    void print_add(int i){ 
      std::cout << i << '\n'; 
    }
};

typedef std::function<void(int)> fp;

void test(fp my_func)
{
  my_func(5);
}

int main(){
    Foo foo;
    test(foo.print_add);
    return 0;
}

Compiler Error:

 error: cannot convert 'Foo::print_add' from type 'void (Foo::)(int)' to type 'fp {aka std::function<void(int)>}'
     test(foo.print_add);

How can I make this work, i.e how can I pass a member function as a parameter?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Kam
  • 5,878
  • 10
  • 53
  • 97
  • Possible duplicate of [Using generic std::function objects with member functions in one class](https://stackoverflow.com/questions/7582546/using-generic-stdfunction-objects-with-member-functions-in-one-class) – sudo rm -rf slash Jul 09 '19 at 08:51

3 Answers3

16

print_add is a non-static member function of foo, which means it must be invoked on an instance of Foo; hence it has an implicit first argument, the this pointer.

Use a lambda that captures the foo instance and invokes print_add on it.

Foo foo;
test([&foo](int i){ foo.print_add(i); });

Another option is to use std::bind to bind the foo instance:

test(std::bind(&Foo::print_add, &foo, std::placeholders::_1));

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Can you explain a bit how this works please? i.e. if this is first argument then how come it is the second one in bind(...)? – Kam May 30 '14 at 19:31
  • 1
    @Kam The first argument to `bind` is a pointer to the member function. The second argument onward are the ones the get passed to that member function when you invoke the `std::function`. Thus the pointer to `foo` gets passed as the first argument to `print_add` (the `this` pointer) when you invoke callable within `test`. The `placeholders::_1` means that you'll supply that argument later, and the first argument you give to the callable (`5`) will be passed along as the second argument to `print_add` (since that's the position the placeholder appears at). – Praetorian May 30 '14 at 19:32
  • @Kam [This page](http://en.cppreference.com/w/cpp/utility/functional/bind) has an excellent example on using `bind` and shows how moving placeholders can be used to achieve different results. – Praetorian May 30 '14 at 19:37
  • Last Question :) which one is preferred here? lambda or bind? performance wise? – Kam May 30 '14 at 19:52
  • @Kam You're sticking both into an `std::function`, and there is a [performance overhead](https://stackoverflow.com/questions/5057382/what-is-the-performance-overhead-of-stdfunction) associated with using one of those, that should be about the same as using a virtual function call. The `bind` itself shouldn't be adding any overhead, but to be honest, I have no idea. I'd personally use the lambda in this case. – Praetorian May 30 '14 at 19:55
9

You need an object to call a non-static member function. Hence, when you want to get a std::function you have several options:

  • make the method static
  • bind an object to the std::function via lambda
  • bind an object to the std::function via custom functor
  • pass the object when the std::function is called

#include <iostream>
#include <functional>

struct A {
    int i = 42;
    int get() const { return i; }    
    
    static int get_static() { return 0; }
};

struct A_functor {
    A a;    
    int operator()() const { return a.get(); }
};

int main() {    

    // static method
    std::function<int()> f1 = &A::get_static;
    std::cout << f1() << "\n";

    // bind an object to the function via lambda
    A a;
    std::function<int()> f2 = [&a](){ return a.get(); };
    std::cout << f2() << "\n";

    // store the object in the std::function
    std::function<int()> f3 = A_functor{};
    std::cout << f3() << "\n";
    // or 
    std::function<int()> f4 = [a = A()](){ return a.get(); };
    std::cout << f4() << "\n";

    // pass the object when the std::function is called
    std::function<int(A&)> f5 = &A::get;
    std::cout << f5(a) << "\n";
    // or 
    std::function<int(A*)> f6 = &A::get;
    std::cout << f6(&a) << "\n";
    
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
4

THE PROBLEM

You cannot directly bind a member-function pointer belonging to type Foo to std::function<void(int)>, specifically because calling a non-static member-function requires an instance of type Foo.

Foo obj; obj.member_function (); // can't call `member_function` without `obj`

Note: You can however bind &Foo::print_add to std::function<void(Foo&, int)> x;, and call it as x(instance_of_Foo, arg);.

Note: It's also possible to bind it to std::function<void(Foo*, int>, which would require a Foo* instead of an lvalue of type Foo.



THE SOLUTION

Instead you can use std::bind to bind an instance of Foo to the member-function in question, such as in the below example:

int main(){
    Foo foo;
    test (std::bind (&Foo::print_add, foo, std::placeholders::_1));
    return 0;
}

Above we bind an instance of Foo named foo to the member-function pointer &Foo::print_add.


The usage of std::placeholders::_1 tells std::bind that we'd like it to generate a function-object that is callable using one (1) argument.

With the above snippet you will have the behaviour that you are currently asking for; my_func(5) will be equivalent of calling foo.print_add (5).


DOCUMENTATION

Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • 2
    The first sentence isn't quite accurate, you can't bind `Foo::print_add` to `std::function` but can bind it to `std::function` or `std::function` – Jonathan Wakely May 30 '14 at 21:31