Search code examples
c++std-functionstdbind

mem_fn, bind, function difference when passing parameters


As shown in the code and output below, three phenomena can be observed:

  1. For mem_fn, which is f1 in the code, whether it is passed ff or &ff, the num_ value in the ff object can be modified normally after execution.
  2. For bind, if ff is bound in advance, the num_ value in ff object is not modified after execution; If you use placeholder to pass ff or bound &ff directly, it can be modified as normal.
  3. For function, if the first parameter is specified as Foo, then ff is passed, and the num_ value in the ff object is not modified after execution.

So my problem is that all callable objects are called just like INVOKE(f, t1, t2, ..., tN). Why do the above three differences occur ?

#include <cstdio>
#include <functional>
#include <iostream>
using namespace std;
struct Foo
{
    Foo(int num) : num_(num) {}
    void print_add(int i)   { num_ += i;}
     int num_;
};

int main()
{

//    cout << "check mem_fn" << endl;
    Foo ff(1);
    auto f1 = std::mem_fn(&Foo::print_add);
    f1(ff, 1);
    cout << "mem_fn pass ff, after exec, ff num : " << ff.num_ << endl;
    f1(&ff, 1);
    cout << "mem_fn pass &ff, after exec, ff num : " << ff.num_ << endl;


    auto f2 = std::bind(&Foo::print_add, placeholders::_1, placeholders::_2);
    f2(ff, 1);
    cout << "bind use placeholder_1 to pass ff, after exec, ff num : " << ff.num_ << endl;

    std::function<void(int)> f3 = std::bind(&Foo::print_add, ff, placeholders::_1);
    f3(1);
    cout << "bind pass ff, after exec, ff num : " << ff.num_ << endl;

    std::function<void(int)> f4 = std::bind(&Foo::print_add, &ff, placeholders::_1);
    f4(1);
    cout << "bind pass &ff, after exec, ff num : " << ff.num_ << endl;

    auto f5 = std::bind(f1, placeholders::_1, placeholders::_2);
    f5(ff, 1);
    cout << "bind_mem_fn pass ff, after exec, ff num : " << ff.num_ << endl;

    auto f6 = std::bind(f1, placeholders::_1, placeholders::_2);
    f6(&ff, 1);
    cout << "bind_mem_fn pass &ff, after exec, ff num : " << ff.num_ << endl;


    std::function<void(Foo, int)> f7 = &Foo::print_add;
    f7(ff, 1);
    cout << "function(Foo), pass ff, after exec, ff num : " << ff.num_ <<endl;

    std::function<void(Foo*, int)> f8 = &Foo::print_add;
    f8(&ff, 1);
    cout << "function(Foo*), pass ff*, after exec, ff num : " << ff.num_ <<endl;


}

The output like this:

mem_fn pass ff, after exec, ff num : 2
mem_fn pass &ff, after exec, ff num : 3
bind use placeholder_1 to pass ff, after exec, ff num : 4
bind pass ff, after exec, ff num : 4
bind pass &ff, after exec, ff num : 5
bind_mem_fn pass ff, after exec, ff num : 6
bind_mem_fn pass &ff, after exec, ff num : 7
function(Foo), pass ff, after exec, ff num : 7
function(Foo*), pass ff*, after exec, ff num : 8

Solution

  • What you observe is the difference between making a copy of ff and not making a copy. In all cases, the callables call the member function and the member is modified. Only sometimes you do not see the modification on ff because the modification is on a copy of ff.

    Here:

    std::function<void(Foo, int)> f7 = &Foo::print_add;
    f7(ff, 1);
    // ^^ passed by value
    

    a copy is made. And here

    std::function<void(int)> f3 = std::bind(&Foo::print_add, ff, placeholders::_1);
    //                                       passed by value ^^
    

    as well.


    The difference boils down to the difference between those two:

    void make_a_copy(Foo f) {
           f.print_add(1);
    }
    
    void pass_by_ref(Foo& f) {
           f.print_add(1);
    }
    

    Calling those two looks the same just like f1(ff,1) and f7(ff,1) looks the same, but they do something different:

    Foo ff;
    make_a_copy(ff);   // modifies a copy of ff
    pass_by_ref(ff);   // modifies ff itself