关于C++ Lambda表达式捕获变量作用域的技术疑问
关于C++ Lambda表达式捕获规则的两个疑问解答
第一个问题:Lambda访问外层作用域变量为何能编译运行?
你的教授关于“Lambda表达式捕获变量的可达作用域仅为最内层封装作用域”的说法不准确,属于授课内容的表述偏差,你的代码完全符合C++标准,既不是未定义行为也不是编译器特殊处理。
C++标准中,Lambda的隐式捕获([&]表示引用捕获、[=]表示值捕获)规则是:自动捕获所有在Lambda体内被使用的、且在Lambda定义点可见的自动变量(栈上的局部变量),不管这些变量位于哪一层嵌套作用域,只要在定义Lambda时能通过作用域链访问到,就会被正确捕获。
看你的代码:
int main(int argc, char const *argv[]) { int a = 5; { int b = 10; { int c = 15; { int a = 10; auto f = [&]() { std::cout << a << b << c; }; f(); std::cout << a; } } } return 0; }
Lambda定义时,b和c虽然不在最内层作用域,但属于外层嵌套作用域的可见变量,[&]会自动以引用方式捕获Lambda体内用到的a(优先匹配内层的a)、b、c。由于调用f()时,这些变量都还处于各自的作用域内存活,引用是有效的,所以代码能正常编译运行,输出10101510。
第二个问题:按值捕获已销毁变量的Lambda为何能正常运行?
助教的说法有误,按值捕获的Lambda行为完全符合C++标准,代码能正常运行是合理的。
C++中,Lambda按值捕获变量时,会在Lambda对象内部创建该变量的独立副本,副本的生命周期和Lambda对象一致,和原变量的生命周期无关。
看你的代码:
int main(int argc, char *argv[]) { int x = 7; std::function<void()> foo; { int z = 19; foo = [x, z]() mutable { x = 5; std::cout << x << " " << z << std::endl; }; } foo(); std::cout << x; return 0; }
- 捕获
x和z时,Lambda会复制这两个变量的值到自己内部的成员中,即使原z在离开内层作用域后被销毁,Lambda对象(被存储在foo中)内部的z副本依然存在,调用foo()时访问的是这个副本,而非原变量,所以不会出现未定义行为。 - 加上
mutable修饰后,Lambda允许修改内部的副本变量,所以x = 5修改的是Lambda内部的x副本,外部的x不受影响,最终外部输出7,这也是符合预期的。
内容的提问来源于stack exchange,提问作者kesetovic
相关产品推荐
相关产品推荐

