关于 C++ Lambda 表达式和闭包的一些思考。
定义 MDN 对闭包的定义为:
函数与对其状态即词法环境(lexical environment) 的引用共同构成闭包(closure) 。也就是说,闭包可以让你从内部函数访问外部函数作用域。
闭包在 JavaScript, Lua 等语言中很常见,它们的函数在每次创建时生成闭包,C++ 在增加 Lambda 表达式以后,也可以较为简单的写出闭包(注意:Lambda 表达式不意味着闭包 )。
实例 计数器 在 JavaScript/Lua 中利用闭包写出如下计数器:
1 2 3 4 5 6 7 8 9 let counter = (function ( ) { let count = 0 ; return () => { return ++count; }; })(); console .log(counter()); console .log(counter());
1 2 3 4 5 6 7 8 9 10 local counter = (function () local count = 0 return function () count = count + 1 return count end end )()print (counter()) print (counter())
类似地,我们在 C++ 中实现
1 2 3 4 5 6 7 8 9 10 11 12 #include <iostream> int main () { auto getCounter = [] { int count = 0 ; return [=]() mutable { return ++count; }; }; auto counter = getCounter(); std ::cout << counter() << std ::endl ; std ::cout << counter() << std ::endl ; }
可以看到局部变量 count
在 main
函数中被访问,实现了计数器累加的效果。但是这里还有一个问题:getCounter
函数退栈后,函数中的临时变量会被销毁,那么这个 count
是储存在哪里的? 我们可以输出一下 count
的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> int main () { auto getCounter = [] { int count = 0 ; std ::cout << &count << std ::endl ; return [=]() mutable { std ::cout << &count << std ::endl ; return ++count; }; }; auto counter = getCounter(); std ::cout << counter() << std ::endl ; std ::cout << counter() << std ::endl ; }
发现输出结果如下:
1 2 3 4 5 0x7fffde240f34 0x7fffde240f54 1 0x7fffde240f54 2
可以发现 count
的地址其实已经发生了变化,Lambda 表达式中的 count
已经不是原来函数里的 count
了,变量是和 Lambda 表达式绑定的。
复制 Lambda 表达式 那么如果复制 Lambda 表达式呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> int main () { auto getCounter = [] { int count = 0 ; return [=]() mutable { return ++count; }; }; auto counter = getCounter(); auto copiedCounter = counter; std ::cout << counter() << std ::endl ; std ::cout << counter() << std ::endl ; std ::cout << copiedCounter() << std ::endl ; }
绑定的参数同样会被复制。
交换参数 实现一个函数 swap
,它接受一个 binary
函数,返回交换了此函数两个参数的函数。
1 2 3 4 5 6 7 #include <iostream> int main () { auto sub = [](auto x, auto y) { return x - y; }; auto swap = [](auto f) { return [=](auto x, auto y) { return f(y, x); }; }; std ::cout << swap(sub)(2 , 1 ); }
Once 实现一个函数 once
,它接受一个函数,并将其返回,返回后的函数只有一次有效调用。
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 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <type_traits> int main () { auto once = [](auto f) { bool done = false ; return [=](auto &&... args) mutable { if (done) throw std ::runtime_error("once function cannot be called twice" ); done = true ; return f(args...); }; }; auto f = once([](auto a, auto b, auto mod) { auto func = [](auto a, auto b, auto mod) { auto ret = static_cast <decltype (a)>(1 ); for (; b; b >>= 1 , a = a * a % mod) if (b & 1 ) ret = ret * a % mod; return ret; }; if constexpr (std ::is_same_v<int , decltype (a)> || std ::is_same_v<unsigned int , decltype (a)>) { return func(static_cast <std ::uint64_t >(a), b, mod); } else { return func(a, b, mod); } }); try { std ::cout << f(2 , 999 , 998244353 ) << std ::endl ; std ::cout << f(2 , 999 , 998244353 ) << std ::endl ; } catch (std ::runtime_error e) { std ::cout << e.what() << std ::endl ; } }
输出为:
1 2 510735315 once function cannot be called twice