Wang's blog

std::function与std::bind

Published on

std::function

类模板std::function是通用多态函数封装器。std::function的实例能存储、复制及调用任何可调用目标,包括:

  • 函数/函数指针
  • 函数对象
  • lambda表达式
  • 成员函数指针
  • 数据成员指针

作用

  • 调用者可使用统一的方式调用可调用目标,无需关心绑定的具体类型
  • 一种类型的std::function对象可绑定多种类型的可调用目标,实现多态的效果(例如用于回调函数)

例子

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

void print_num(int i)
{
    std::cout << i << '\n';
}

struct PrintNum
{
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};

int main()
{
    // 存储函数
    std::function<void(int)> f1 = print_num;
    f1(-9);

    // 存储lambda表达式
    std::function<void()> f2 = []() { print_num(42); };
    f2();

    // 存储std::bind的结果
    std::function<void()> f3 = std::bind(print_num, 31337);
    f3();

    // 存储成员函数指针
    std::function<void(const Foo &, int)> f4 = &Foo::print_add;
    const Foo foo(314159);
    f4(foo, 1);
    f4(314159, 1);

    // 存储数据成员指针
    std::function<int(Foo const &)> f5 = &Foo::num_;
    std::cout << f5(foo) << '\n';

    // 存储成员函数指针,绑定对象
    using std::placeholders::_1;
    std::function<void(int)> f6 = std::bind(&Foo::print_add, foo, _1);
    f6(2);

    // 存储成员函数指针,绑定对象指针
    std::function<void(int)> f7 = std::bind(&Foo::print_add, &foo, _1);
    f7(3);

    // 存储函数对象
    std::function<void(int)> f8 = PrintNum();
    f8(18);
}

std::bind

std::bind函数将可调用目标与指定参数进行绑定,并以函数对象的形式保存。调用此函数对象相当于使用绑定的参数调用原可调用目标。如果有的参数不能在绑定时确定,可以使用std::placeholders中的占位符占位,并延迟到调用时再传入。

作用

  • 提前将参数绑定,调用时无需再次传入,并且可以多次调用
  • 将一个可调用目标的接口形式转换成另一种形式,以适应某种接口要求(如注册回调函数)

例子

void f(int n1, int n2, int n3, const int &n4, int n5)
{
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}

int g(int n1)
{
    return n1;
}

struct Foo
{
    void print_sum(int n1, int n2)
    {
        std::cout << n1 + n2 << '\n';
    }

    int data = 10;
};

int main()
{
    // 占位符,如_1, _2, _3...
    using namespace std::placeholders;

    // 参数重排序与按引用传递
    int n = 7;
    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
    n = 10;
    // 相当于调用f(2, 42, 1, n, 7),1001未使用
    f1(1, 2, 1001);

    // 使用lambda表达式达成相同效果
    n = 7;
    auto lambda = [&ncref = n, n](auto a, auto b, auto /*未使用*/)
    {
        f(b, 42, a, ncref, n);
    };
    n = 10;
    // 相当于调用f(2, 42, 1, n, 7),1001未使用
    lambda(1, 2, 1001);

    // 嵌套bind子表达式共享占位符
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    // 相当于调用f(12, g(12), 12, 4, 5)
    f2(10, 11, 12);

    // 绑定成员函数指针
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);

    // 绑定成员函数指针的mem_fn
    auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
    auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
    f4(5);

    // 绑定数据成员指针
    auto f5 = std::bind(&Foo::data, _1);
    std::cout << f5(foo) << '\n';

    // 绑定数据成员指针的mem_fn
    auto ptr_to_data = std::mem_fn(&Foo::data);
    auto f6 = std::bind(ptr_to_data, _1);
    std::cout << f6(foo) << '\n';

    // 使用智能指针调用被引用对象的成员
    std::cout << f6(std::make_shared<Foo>(foo)) << ' '
              << f6(std::make_unique<Foo>(foo)) << '\n';
}