您的位置:首页 > 编程语言 > C语言/C++

[C++ 11札记]: std::bind

2018-03-06 15:44 274 查看
上一篇文章中,我们提到可调用对象(callable object),其中一种就是std::bind表达式。在这篇文章中,我们来谈谈std::bind表达式。

关于std::bind的定义如下:

template< class F, class... Args >
/*unspecified*/ bind( F&& f, Args&&... args );

template< class R, class F, class... Args >
/*unspecified*/ bind( F&& f, Args&&... args );


关于其解释可以参看:http://en.cppreference.com/w/cpp/utility/functional/bind

看上去不好理解,咱们还是以一个简单的例子来进行说明,毕竟一个例子胜过千言万语。比如有如下函数实现两个整数相加:

int add(int x, int y) {
return x + y;
}


假如现在我们要实现某个整数加上6的函数功能,除了写一个类似的函数:

int add1(int x) {
return x + 6;
}


之外,我们还可以复用上面的add函数:

auto add1 = bind(add, placeholders::_1, 6);
cout << add1(2) << endl; // print 8


简单说,bind就是一个函数包装器(function wrapper),在一个通用化函数的基础上,固定一个或多个输入参数,包装成一个更加简化的函数。其好处有:

代码复用。上面的例子过于简单,可能没有表现出这个好处。但我们可以想象一下加入上面的add函数实现了很复杂的逻辑,通过copy代码的方式实现类似功能,极其容易引入bug。

易于维护,这其实也是代码复用带来的好处,代码逻辑写在一处比分散在多处更容易维护。

如果说这两点好处还不足以说服我们使用std::bind,那接下来我们要探讨的用法才是std::bind的最大用途。

上一篇文章中,我们曾提过对象的成员函数无法与函数指针相容,主要原因在于类的成员函数都包含有一个隐含的this参数。比如:

class Simple
{
private:
int m_id;

public:
Simple(int id)
{
setID(id);
}

void setID(int id) { m_id = id; }
int getID() { return m_id; }
};


其中的setID成员函数经过编译器的处理,等价于:

void setID(Simple* const this, int id) { this->m_id = id; }


调用代码

simple.setID(2);


经过编译器处理,成为:

setID(&simple, 2);


这一切都是编译器在处理,对于实现者和调用者并不需要关心这些细节。回想之前的std::bind,我们是否可以将指针绑定到成员函数而包装成另一个函数呢?

#include <functional>
#include <iostream>

struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};

void apply(std::function<void()> func) {
func();
}

int main() {
Foo foo1{1};
Foo foo2{2};

apply(std::bind(&Foo::f, &foo1));
apply(std::bind(&Foo::g, &foo2));
}


在上述代码中,我们将Foo的成员函数包装成了

std::function<void()>


这样的类型,从而可以用在回调等场景。

当然,在实际项目的代码中,还有很多bind的用途,比如chromium项目中就有大量的bind和callback,虽然里面并不是使用的std::bind,而是使用自己定义的base::bind,但在原理上是差不多的。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: