c++ - 在嵌套 lambda 的情况下如何初始化 lambda 捕获?
问题描述
好的,这就是 n3337.pdf 中的 [expr.prim.lambda]p16 的内容。下面的代码作为一个例子给出:
int a = 1, b = 1, c = 1;
auto m1 = [a, &b, &c]() mutable
{
auto m2 = [a, b, &c]() mutable
{
std::cout << a << b << c; // Shouldn't this print 113 or 133?
a = 4; b = 4; c = 4;
};
a = 3; b = 3; c = 3;
m2();
};
a = 2; b = 2; c = 2;
m1();
std::cout << a << b << c; // Okay, this prints 234
并且它将生成以下输出:
123234
但是,我理解 [expr.prim.lambda] 中文本的方式(这在某种程度上显然存在缺陷),我觉得输出应该是113234
,特别是 print in 的b
值m2
。以下是我的理解/解释:
当在std::cout << a << b << c;
内部执行时m2
,根据 [expr.prim.lambda]p16 (强调我的):
如果一个 lambda 表达式 m2 捕获一个实体,并且该实体被一个直接封闭的 lambda 表达式 m1 捕获,则 m2 的捕获转换如下:
—如果 m1 通过副本捕获实体,则 m2 捕获对应的 m1 闭包类型的非静态数据成员;
因此,a
内部应将生成的成员捕获到闭包类型m2
中对应的捕获。由于in是按副本捕获的,并且in也是按副本捕获的,所以 in的值应该是。a
m1
a
m1
a
m2
a
m2
1
该标准继续说(再次强调我的):
—如果 m1 通过引用捕获实体,则 m2 捕获由 m1 捕获的相同实体。
我相信这里的“相同实体”是指m1
通过引用捕获的实体,并且当被m2
它捕获时应该是 - 如果它是通过引用捕获,则对同一实体的引用,或者如果它是通过副本捕获,则为它的副本。
因此 for b
inm2
应指b
两个 lambdas 之外的定义。b
in then的值m2
应该是1
asb
也被副本捕获。
我哪里错了?b
更具体地说,内部何时m2
初始化?
解决方案
首先,请注意,根据 C++11 [ expr.prim.lambda[]
] 第 14 段(或C ++17 [ expr.prim.lambda.capture] 第 10 段)。
您从 C++11 [expr.prim.lambda]/16(或 C++17 [expr.prim.lambda.capture]/13 中的相同内容)中引用的部分仅更改捕获的实体,而不更改捕获的类型捕获。因此,在示例中,用于初始化的内部 lambda通过复制从原始定义中m2
捕获。b
然后,注意 C++11 [expr.prim.lambda]/21:
当计算lambda 表达式时,复制捕获的实体用于直接初始化结果闭包对象的每个相应的非静态数据成员。
(C++17 [expr.prim.lambda.capture]/15开头相同,但为init-capture语法添加了额外的措辞,如[var=init]
.)
在这个例子中,内部lambda-expression的初始化m2
被求值,闭包对象的成员 forb
被初始化,每次都m1.operator()
被调用,而不是按照lambda-expression在代码中出现的顺序。由于 lambda for通过副本m2
捕获原始内容,因此它在调用时b
获取该值。如果被多次调用,则每次的初始值都可能不同。b
m1
m1
b
推荐阅读
- java - 是否可以为实体设置自定义生成的字符串 ID?
- javascript - 在光标位置绘制粒子的 Javascript 将粒子发送到底部
- database - 我们应该在关系数据库中的日期或日期时间列上创建索引吗?
- c# - 对 DbContext 实例具有强制依赖关系是不是很糟糕?
- vba - VBA 拆分代码和传递参数
- sql - 左连接与案例
- python - 为什么 Flask-SocketIO 有 `http` 协议而不是 `ws`?
- hibernate - Hibernate 的 @Where 注释是否适用于 Spring Data JPA?
- python - 在没有 PhantomJS 的 AWS Lambda 中运行 Beautiful Soup
- ruby-on-rails - Rails simple_form gem 正在为预填充的输入添加绿色边框