C++11实现C++14捕获列表变量初始化
前言
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
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
21void 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
11void 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
11struct 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)。这里不讨论其具体实现。