前言

C++11引入了lambda表达式,让C++的<functional><algorithm>的使用更方便和具有可读性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <algorithm>

struct KeyValue {
int key;
int value;
};

std::vector<KeyValue> kvs{...};

// Before C++11
inline bool KvCmp(KeyValue const &x, KeyValue const &y) throw() {
return x.key < y.key;
}

// OR
struct KvCmp {
inline bool operator()(KeyValue const &x, KeyValue const &y) throw() {
return x.key < y.key;
}
};

// After C++11
std::sort(kvs.begin(), kvs.end(), [](KeyValue const &x, KeyValue const &y) {
return x.key < y.key;
})

我认为这是个对于C++迈入现代化的一个重要特性,但是C++11的lambda表达式并不完全,比如不支持捕获列表进行变量初始化,这对于一些场景会损失性能(比如下面这个)。这点在C++14得到了弥补。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void g(std::function<void()> f) {

}

// obj 不再需要使用了,可以移动
void f(T &obj) {
g([o = std::move(obj)] {
//...
});
}

// C++11要实现类似的效果通过lambda语法是无法做到的
// 你可能认为引用捕获是等效的
// 但等效前提是该回调在g()中必须立即被调用
// 考虑,基于事件的网络库,注册的回调会在事件触发时
// 调用,因此是异步的
void f(T &obj) {
g([&obj] {
//...
});
}

std::bind等效实现

std::bind是早在tr1草案中就已经很受欢迎的callback wrapper,在C++11中和std::function正式纳入标准。
有人说在引入了lambda的C++11已经没有必要引入std::bind了,我表示这过于狭隘,或者说经验不足,正如上述所说,C++11的lambda还不完善,在名为C++14的“补丁”下lambda才趋于完善,因此C++11需要std::bind加强lambda,弥补其不足。
对于C++14的该特性,可以通过std::bind等效实现,结合移动语义和引用:

1
2
3
4
5
6
7
8
9
10
11
void g(std::function<void()> f) {

}

void callback(T &obj) {
//...
}

void f(T &obj) {
g(std::bind(&callback, std::move(obj)));
}

你可以想象std::bind将参数列表作为内部函数对象的数据成员:
1
2
3
4
5
6
7
8
9
10
11
struct Bind_Callback {
template<typename U>
Bind_Callback(void(callback_*)(T&) callback, U&& obj)
: callback_(callback)
, obj_(std::forward<U>(obj))
{
}

void(callback_*)(T&);
T obj_;
};

当然这只是想象,真实的std::bind实现必然要利用一些模板技巧(包括但不限于类型擦除,元编程,traits)。这里不讨论其具体实现。