Lambda 捕获列表:如果不捕获整个对象,就不可能按值捕获对象的成员字段?

Lambda capture list: capturing object's member field by value not possible without capturing the whole object?

本文关键字:对象 成员 字段 不可能 如果不 Lambda 列表      更新时间:2023-10-16

以下代码

void CMainWindow::someMethod(const CLocationsCollection& parentItem)
{
    auto f = [this, parentItem.displayName](){};
}

给我一个错误:

错误C2143:语法错误:'.'之前缺少']'

如果我想通过ref捕获parentItem.displayName,我将为它创建一个非依赖的别名标识符:

const QString& name = parentItem.displayName;
auto f = [this, &name](){}; // Or should it be [this, name] ?

但是我需要按值捕获它,我不想捕获整个parentItem,因为它很重。有解决方案吗?

p。捕获列表中的名称必须是标识符。是不是parentItem.displayName(作为一个整体)一个标识符?为什么它不能被编译器正确解析?

注释:本文中的所有标准参考均取自c++标准草案n3337。


介绍

标准规定捕获必须是&, =, this标识符,或者标识符前面是&

5.1.2 Lambda表达式 [expr.prim.lambda]

capture-list:
  capture ..._opt
  capture-list , capture ..._opt
capture:
  identifier
  & identifier
  this

由于parentItem.displayName不是标识符,而是"类成员访问表达式"。当编译器拒绝你的代码片段时是正确的。

5.2.5p1 类成员访问 [expr.ref]

后缀表达式后跟点.或箭头->,可选后跟关键字template(14.2),然后后跟id-expression,是一个后缀表达式。66该求值的结果与id-expression一起决定整个后缀表达式的结果。


如果有一种方法可以用表达式初始化捕获就好了

c++ 14中,能够使用init-capture来规避手头的问题,如下面的代码片段所示。它将创建一个名称为display_name的捕获,并使用parentItem.displayName的值进行初始化。

[display_name = parentItem.displayName](){ ... };

那么 c++ 11呢?

遗憾的是,在 c++ 11中没有这样的功能。因此,该问题的解决方案是对数据成员创建一个本地引用,然后在创建lambda时捕获该引用。
auto& lmb_display_name = parentItem.displayName;
[lmb_display_name](){ ... }; // the lambda will have a copy of `parentItem.displayName`


注释:您的原始帖子似乎暗示,在lambda的捕获列表中R的名称将使lambda包含对R引用的引用,这是不正确的,从这个代码片段可以看出。