问题 理解C ++ 0x lambda捕获


在最近的一个C ++ 0x草案(n3225.pdf)中,我们可以找到5.1.2 / 10:

使用通常的非限定名称查找规则(3.4.1)查找捕获列表中的标识符;每个这样的查找应该找到一个变量,其自动存储持续时间在本地lambda表达式的到达范围内声明。如果实体(即变量或此实体)出现在lambda表达式的捕获列表中,则称其被明确捕获。

似乎 对我来说相当严格。例如,它 似乎 对我来说,以下事情是不允许的:

int global;

struct s {
    int x;
    void memfun() {
        [x,global]{};
    }
};

以来 x 不一定是具有自动存储的变量,也不是 global。请注意,此捕获子句的目的是让lambda对象存储一个 复制 的 x 和 global 如果在以后阶段改变它们可能是合乎需要的。我已经知道一个替代方案:

int global;

struct s {
    int x;
    void memfun() {
        int copyx = x;
        int copyglobal = global;
        [copyx,copyglobal]{};
    }
};

但这归结为额外的副本和额外的锅炉板只是为了捕获 x 和 global 作为副本。

另外,我在最新的草稿中找不到任何结论,如果我们在capture子句中命名本地引用会发生什么:

int main() {
    int  i = 0;
    int &r = i;
    assert([r]{return &r;}() != &i);
}

lambda对象“复制引用”还是“复制int”?如果它通过副本捕获引用的对象,这可以节省我们之前的解决方案中的其他副本。

GCC显然支持所有这些示例,并在最后一种情况下存储一个int的副本(这是可取的,恕我直言)。但我想知道这是否实际上是根据C ++ 0x草案的预期行为,或者只是一个编译器扩展,分别是一个实现bug。

编辑:

templatetypedef指出5.1.2 / 14,它解释了在capture-clause中命名引用时会发生什么。据我所知,这允许我们在第一个例子中使用以下解决方法:

int global;

struct s {
    int x;
    void memfun() {
        auto& cx = x;
        auto& cglob = global;
        [cx,cglob]{};
    }
};

蒂亚, sellibitze


9370
2018-03-29 09:06


起源

注意:C ++ 14扩展了可能的捕获子句集 - sellibitze


答案:


根据您发布的内容,您的第一个示例似乎是非法的,因为捕获的变量都没有自动持续时间。 但是,您可以轻松解决此问题。要捕获数据成员,您可以捕获它,并且不需要捕获全局,因为您可以直接引用它。

编辑:正如您所指出的,这不会创建您要捕获的值的本地副本。 要在制作副本时捕获这些变量,您可以捕获它,然后在lambda中显式创建数据成员的本地副本。

关于捕获引用的第二个问题,§5.1.2/ 14表示通过复制捕获引用类型的变量将创建引用值的副本,而不是创建引用的副本。因此,lambda将拥有自己的值,该引用在创建时引用了引用的值。


11
2018-03-29 09:10



命名点 x 和 global 在capture子句中是让lambda对象存储一个 复制。这可能是理想的情况 ::global 和 s::x 改变他们的价值观所以,不,捕捉 this 不会导致预期的行为。 - sellibitze
你是对的;我刚刚更新了我的答案。感谢您指出了这一点! - templatetypedef
感谢您指出5.1.2 / 14。看起来如果我们有兴趣通过复制捕获其他东西,我们可以引入对非自动变量的本地引用作为解决方法。 - sellibitze
在lambda中创建副本仍然不同于在lambda创建时创建副本。并非难以这样做,只是不能使用您的提案“解决方案”。 - Ben Voigt