好的,这就是 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
,特别是 b
的值印于 m2
.以下是我的理解/解释:当
std::cout << a << b << c;
在 m2
内执行,根据 [expr.prim.lambda]p16(强调我的):If a lambda-expression m2 captures an entity and that entity is captured by an immediately enclosing lambda expression m1, then m2’s capture is transformed as follows:
— if m1 captures the entity by copy, m2 captures the corresponding non-static data member of m1’s closure type;
因此,
a
内m2
将生成的成员捕获到对应的a
在闭包类型中捕获 m1
.自 a
在 m1
按拷贝捕获,和 a
在 m2
也通过拷贝捕获,a
的值在 m2
应该是 1
.标准接着说(再次强调我的):
— if m1 captures the entity by reference, m2 captures the same entity captured by m1.
我相信这里的“同一实体”是指
m1
捕获的实体。通过引用,当被 m2
捕获时它应该是 - 如果是按引用捕获,则是对同一实体的引用,如果是按拷贝捕获,则是对同一实体的引用。因此对于
b
在 m2
应引用b
在两个 lambda 表达式之外定义。 b
的值在 m2
那么应该是 1
如 b
也被复制捕获。我哪里错了?更具体地说,什么时候是
b
内m2
初始化?
最佳答案
首先,请注意,根据 C++11 [expr.prim.lambda] 第 14 段(或 C++17 [expr.prim.lambda.capture] paragraph 10),捕获是通过复制还是通过引用仅取决于 lambda 表达式自己的 lambda 引入器(初始 []
部分)。
您从 C++11 [expr.prim.lambda]/16(或在 C++17 [expr.prim.lambda.capture]/13 中相同)引用的片段仅更改捕获的实体,而不更改捕获的实体类型捕获。因此,在示例中,用于初始化 m2
的内部 lambda 通过复制从原始定义中捕获 b
。
然后,请注意 C++11 [expr.prim.lambda]/21:
When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object.
(C++17 [expr.prim.lambda.capture]/15 开头相同,但为初始化捕获语法添加了额外的措辞,如
[var=init]
。)在示例中,每次调用
m2
时,都会计算用于初始化 b
的内部 lambda 表达式,并初始化 m1.operator()
的闭包对象成员,而不是按照 lambda 表达式出现在代码中的顺序。由于 m2
的 lambda 通过复制捕获原始 b
,因此它在调用 b
时获取 m1
的值。如果多次调用 m1
,则 b
的初始值每次都可能不同。
关于c++ - 在嵌套 lambda 的情况下如何初始化 lambda 捕获?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62047185/