“多年以前"写过一篇关于C++闭包的文章,现在看来基本是关于lambda
的介绍。随着经验的增加,对闭包的理解更加深刻。在上一篇文章中,介绍了我编写的一个tiny_cmdline
库,随后我对一些tiny库更感兴趣,便想着做了一个集合,取名为tinylib
,其中有一个tiny库是关于match的。调用方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
using namespace tiny_utility;
auto match_op = match(
"1" | [](const auto &i) { ASSERT_EQ(i, "1"); }, // no line break
"2" | [](const auto &i) { ASSERT_EQ(i, "2"); }, // no line break
"3" | [](const auto &i) { ASSERT_EQ(i, "3"); }, // no line break
std::set<std::string>{"5", "6"} | [](const auto &i) { ASSERT_NE(i, "4"); }, // no line break
placeholder | [](const auto &i) { ASSERT_TRUE(false); } // no line break
);
// for (const auto &key : std::vector<std::string>{"1", "2", "3", "4" , "5", "6"}) {
for (const auto &key : std::vector<std::string>{"1", "2", "3", "5", "6"}) {
match_op(key);
}
|
考虑到case key的各种类型兼容,目前的match类是运行时构建的,并且是一个模板类(key的类型是各种各样的)。
如果在实际使用中,比如有某个query接口,是不会考虑在query接口中频繁构建match类的,一般会在构造或者其他初始化接口中构造一次,然后在query接口中多次调用。
但是针对match构造一次的情况,如何保存呢?不可能考虑在类的成员变量中写一长串的类型,并且这样也不适合match的扩展。
用闭包可以解决这问题,闭包的一个特点是可以隐藏一些局部变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// query.h
class Query {
// ...
std::function<void(const std::string &)> match_op_;
};
// query.cpp
#include "tiny_match.h"
Query::Query() {
auto match_op = match(
"1" | [](const auto &i) { ASSERT_EQ(i, "1"); }, // no line break
"2" | [](const auto &i) { ASSERT_EQ(i, "2"); }, // no line break
"3" | [](const auto &i) { ASSERT_EQ(i, "3"); }, // no line break
std::set<std::string>{"5", "6"} | [](const auto &i) { ASSERT_NE(i, "4"); }, // no line break
placeholder | [](const auto &i) { ASSERT_TRUE(false); } // no line break
);
match_op_ = [match_op](const std::string &key) { match_op(key); };
}
|
这样,Query类通过保存一个闭包函数,避免在类中声明可变长参数的match类型。
并且,也能发现可以在query.h声明文件中,隐藏tiny_match.h的头文件,只需要在query.cpp中包含即可。
tiny_match.h的实现在: https://github.com/caibingcheng/tinylib/blob/master/tiny_utility/tiny_match.h
根据match这个案例, 再来理解闭包这个概念:
- 可以捕获上层函数的局部变量, 比如
match_op_
捕获了match_op
- 可以隐藏局部变量, 比如
match_op
, 外部通过某些接口访问match_op_
时, 是无法感知match_op
的存在的
- 通过2, 闭包可以隐藏实现细节
- 一个"神奇"的通道, 外部接口居然可以访问到内部的局部变量, 尽管是这个局部变量的复制体