首页 > 解决方案 > 在嵌套 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 的bm2。以下是我的理解/解释:

当在std::cout << a << b << c;内部执行时m2,根据 [expr.prim.lambda]p16 (强调我的):

如果一个 lambda 表达式 m2 捕获一个实体,并且该实体被一个直接封闭的 lambda 表达式 m1 捕获,则 m2 的捕获转换如下:

如果 m1 通过副本捕获实体,则 m2 捕获对应的 m1 闭包类型的非静态数据成员;

因此,a内部应将生成的成员捕获到闭包类型m2中对应的捕获。由于in是按副本捕获的,并且in也是按副本捕获的,所以 in的值应该是。am1am1am2am21

该标准继续说(再次强调我的):

如果 m1 通过引用捕获实体,则 m2 捕获由 m1 捕获的相同实体

我相信这里的“相同实体”是指m1通过引用捕获的实体,并且当被m2它捕获时应该是 - 如果它是通过引用捕获,则对同一实体的引用,或者如果它是通过副本捕获,则为它的副本。

因此 for binm2应指b两个 lambdas 之外的定义。bin then的值m2应该是1asb也被副本捕获。

我哪里错了?b更具体地说,内部何时m2初始化?

标签: c++c++11lambdalanguage-lawyer

解决方案


首先,请注意,根据 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获取该值。如果被多次调用,则每次的初始值都可能不同。bm1m1b


推荐阅读