std::function and bind binders for C++11

Callable object

In C++, there is a concept of “callable object”. To be precise, callable objects have the following definitions:

1. It is a function pointer

2. It is a class object (functor) with operator() member function

3. It is a class object that can be converted into a function pointer.

4. It is a class member (function) pointer

#include <iostream>
using namespace std;

void func(void)
{}

struct Foo
{
    void operator()(void){}
};

struct Bar
{
    using fr_t = void(*)(void);

    static void func(void)
    {
        printf("%s %s %d\\
", __FILE__, __func__, __LINE__);
    }

    operator fr_t(void)
    {
        return func;
    }
};

struct A
{
    int a_;

    void mem_func(void)
    {
        printf("%s %s %d\\
", __FILE__, __func__, __LINE__);
    }
};

int main()
{
    void (*func_ptr)(void) = & amp;func; ///1. Function pointer
    func_ptr();

    Foo foo;
    foo(); ///2. Functor

    Bar bar;
    bar(); ///3. Class object that can be converted into a function pointer

    void (A::*mem_func_ptr)(void) = & amp;A::mem_func; ///4. Class member function pointer

    int A::*mem_obj_ptr = & amp;A::a_; ///Class member pointer

    A aa;
    (aa.*mem_func_ptr)();
    aa.*mem_obj_ptr = 123;

    return 0;
}

As can be seen from the above, except for class member pointers, the objects involved in the above definition can be called like a function.

In C++11, these objects like in the above example (func_ptr, foo, bar, mem_func_ptr, mem_obj_ptr) are called callable objects. Correspondingly, the types of these objects are collectively called “callable types”.

C++11 now unifies the respective operations of callable objects through std::function and std::bind.

Callable object wrapper—std::function

std::function is a wrapper around a callable object. It is a class template that can hold all callable objects except class member (function) pointers. By specifying its template parameters, it can handle functions, function objects, and function pointers in a unified way, and allows saving and delaying execution of them.

Basic usage of std::function

#include <iostream>
#include <functional>
using namespace std;

void func(void)
{
    cout << __FUNCTION__ << endl;
}

classFoo
{
public:
    static int foo_func(int a)
    {
        cout << __FUNCTION__ << "(" << a << ") ->: ";
        return a;
    }
};

class bar
{
public:
    int operator()(int a)
    {
        cout << __FUNCTION__ << "(" << a << ") ->: ";
        return a;
    }

};

int main()
{
    function<void(void)> fr1 = func; ///Bind a normal function
    fr1();

    ///Bind a static member function of a class
    std::function<int(int)> fr2 = Foo::foo_func;
    cout << fr2(123) << endl;

    Bar bar;
    fr2 = bar;
    cout << fr2(123) << endl;

    return 0;
}

The running results are as follows:

From the above we can see how to use std::function. When we fill in the appropriate function signature for std::function (i.e. a function type, which only needs to include the return value and parameter list), it becomes a A “function wrapper” that can accommodate all such calling methods.

Example of std::function as a callback function

#include <iostream>
#include <functional>
using namespace std;

class A
{
public:
    A(const std::function<void()> & amp; f):callback_(f){}

    void notify(void)
    {
        callback_();
    }

private:
    std::function<void()> callback_;
};

classFoo
{
public:
    void operator()(void)
    {
        cout << __FUNCTION__ << endl;
    }
};

int main()
{
    Foo foo;
    A aa(foo);

    aa.notify();

    return 0;
}

As you can see from the above example, std::function can replace the role of function pointers. Because it can save delayed execution of functions, it is more suitable as a callback function.

std::function as function parameter example

#include <iostream>
#include <functional>
using namespace std;

void call_when_even(int x, const std::function<void(int)> & amp; f)
{
    if (!(x & amp; 1))
    {
        f(x);
    }
}

void output(int x)
{
    cout << x << " ";
}

int main()
{
    for(int i = 0; i < 10; i + + )
    {
        call_when_even(i, output);
    }
    cout << endl;

    return 0;
}

As you can see from the above example, std::function is more flexible and convenient than ordinary function pointers.

std::bind binder

std::bind is used to bind a callable object with its parameters. The result after binding can be saved using std::function and delayed calling until whenever we need it.

Generally speaking, it has two main functions:

1. Bind the callable object and its parameters into a functor.

2. Convert a multi-element (number of parameters is n, n>1) callable object into a one-element or (n-1) element callable object, that is, only bind some parameters.

Basic usage of std::bind

#include <iostream>
#include <functional>
using namespace std;

void call_when_even(int x, const std::function<void(int)> & amp; f)
{
    if (!(x & amp; 1))
    {
        f(x);
    }
}

void output(int x)
{
    cout << x << " ";
}

void output_add_2(int x)
{
    cout << x + 2 << " ";
}


int main()
{
    {
        auto fr = std::bind(output, std::placeholders::_1);
        for(int i = 0; i < 10; i + + )
        {
            call_when_even(i, fr);
        }
        cout << endl;
    }

    {
        auto fr = std::bind(output_add_2, std::placeholders::_1);
        for(int i = 0; i < 10; i + + )
        {
            call_when_even(i, fr);
        }
        cout << endl;
    }

    return 0;
}

We use std::bind to control the final execution result by binding different functions outside the function.

We use auto fr to save the return result of std::bind, because we don’t care about the real return type of std::bind (in fact, the return type of std::bind is a functor type defined internally by stl), we only need to know it Is a functor that can be directly assigned to a std::function. Of course, it is also possible to directly use the std::function type to save the return value of std::bind.

std::placeholders::_1 is a placeholder, which means that this position will be replaced by the first parameter passed in when the function is called.

Placeholder for std::bind

#include <iostream>
#include <functional>
using namespace std;

void output(int x, int y)
{
    cout << x << " " << y << endl;
}

int main()
{
    ///Output 1,2
    std::bind(output, 1, 2)();

    ///Output 2,2
    std::bind(output, std::placeholders::_1, 2)(2);

    ///Output 3,444
    std::bind(output, 3, std::placeholders::_1)(444);

    ///error, there is no second parameter when calling
    ///std::bind(output, 3, std::placeholders::_2)(333);

    ///The first parameter is swallowed when calling
    ///Input 1, 333
    std::bind(output, 1, std::placeholders::_2)(7, 333);

    ///Enter 7, 333
    std::bind(output, std::placeholders::_1, std::placeholders::_2)(7, 333);

    return 0;
}

The above method calls the return result of std::bind directly. As you can see, std::bind can directly bind all parameters of the function, or it can also bind some parameters.

When binding some parameters, std::placeholders are used to determine which parameter the empty parameter will belong to when the call occurs.

std::bind and std::function are used together

#include <iostream>
#include <functional>
using namespace std;

class A
{
public:
    int i_ = 0;

    void output(int x, int y)
    {
        cout << x << " " << y << endl;
    }
};

int main()
{
    A a;

    std::function<void(int, int)> fr = std::bind( & amp;A::output, & amp;a, std::placeholders::_1, std::placeholders::_2);

    fr(1, 2);

    std::function<int & amp; (void)> fr_i = std::bind( & amp;A::i_, & amp;a);

    fr_i() = 123;

    cout << a.i_ << endl;

    return 0;
}

The type of fr is std::function. We bind the pointer of the member function output of A to a by using std::bind, and convert it into a functor and store it in fr.

After that, std::bind binds the pointer of member i_ of A to a, and the returned result is stored in std::function, and this member can be modified and accessed when needed. .