1. 介绍

std::bind 是C++11由Boost引进的函数适配器。它以函数作为输入参数,返回一个函数对象,输入函数的一个或者多个参数可以被绑定。

2. 使用

std::bind可以用于绑定很多对象,包括:函数,类的成员函数,类的成员变量的指针等。以下举几个简单的例子来展示它的用法。

2.1 绑定函数

#include <iostream>
#include <functional>

int add(int a, int b, int& c)
{
    return a+b+c;
}

int main()
{
    int c = 10;
    auto f = std::bind(add, 1, 2, c);
}

2.2 绑定类的成员变量的指针

#include <iostream>
#include <functional>

class Foobar
{
    public:
        int m_;
};

int main()
{
    Foobar foobar;
    foobar.m_ = 123;

    auto f = std::bind(&Foobar::m_, &foobar); //注意:这里第一个参数传的是类的成员函数的地址
                                              //第二个传的是实例的地址(如果直接传实例会调用其拷贝构造函数)
    std::cout << f() << std::endl;
}

输出:123

2.3 绑定类的成员函数

#include <iostream>
#include <functional>

using namespace std::placeholders;

class Foobar
{
    public:
        Foobar():
            value_(0)
        {}

        Foobar(const Foobar &o):
            value_(0) // reset value to 0
        {
            std::cout << "Foobar: copy C'tor" << std::endl;
        }

        int setValue(int v)
        {value_ = v;}

        int printValue()
        {
            std::cout << value_ << "(caller: " << std::showbase << this << ")" << std::endl;
        }

    private:
        int value_;
};

int main()
{
    Foobar foobar;
    foobar.setValue(100);

    // If pass object to bind(), the copy C'tor will be called.
    // A new object is constructed.
    std::cout << "\nPassing Object: " << std::endl;
    auto f = std::bind(&Foobar::printValue, foobar);
    f();

    std::cout << "\nPassing pointer: " << std::endl;
    auto ff = std::bind(&Foobar::printValue, &foobar);
    ff();

    std::cout << "\nPassing reference: " << std::endl;
    auto fff = std::bind(&Foobar::printValue, std::ref(foobar));
    fff();
}

输出:

Passing Object:
Foobar: copy C'tor
0(caller: 0x7ffef109ad70)

Passing pointer:
100(caller: 0x7ffef109ad30)

Passing reference:
100(caller: 0x7ffef109ad30)

引用cppreference中关于bind的说明:

As described in Callable, when invoking a pointer to non-static member function or pointer to non-static data member, the first argument has to be a reference or pointer (including, possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose member will be accessed.

The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.

所以上面的例子中,在传入第一个参数的时候,第一个例子中传入的是foobar对象本身,于是会调用Foobar类的拷贝构造函数来构造一个新的对象,这往往是不希望发生的;而第二个和第三个例子中传入的是foobar的指针和引用,前者是指传递(传递指针,也就是地址),后者是传递引用。在调用发生的时候都会调用foobar中的函数。

另外一个例子是将一个类(A)中的函数设置到另一个类(B),作为类B对A的callback函数:

#include <iostream>
#include <functional>

class Worker
{
    public:

        Worker():
            callback_(nullptr)
        {}

        void doSomething()
        {
            /* doing something... */
            callback();
        }

        void callback()
        {
            if (callback_ != nullptr)
                callback_();
        }

        void setCallback(std::function<void()> f)
        {
            callback_ = f;
        }

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

class Interface
{
    public:
        void notify()
        {
            std::cout << "Notify outside world some event" << std::endl;
        }
};

class Manager
{
    public:

        Manager():
            worker_(),
            interface_()
        {
            worker_.setCallback(std::bind(&Interface::notify, &interface_));
        }

        void doSomething()
        {
            worker_.doSomething();
        }

    private:
        Interface   interface_;
        Worker      worker_;
};

int main()
{
    Manager mgr;

    mgr.doSomething();
}

输出:Notify outside world some event

3. 引用

[1] std::bind – Tutorial and Examples

[2] boost bind.hpp

[3] cppreference