Lambda 捕获列表:如果不捕获整个对象,就无法通过值捕获对象的成员字段?

问题描述 投票:0回答:1

以下代码

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。 S. 捕获列表中的名称必须是标识符。

parentItem.displayName
(作为一个整体)不是一个标识符吗?为什么编译器不能正确解析?

c++ c++11 lambda language-lawyer
1个回答
20
投票

注意:本文中的所有标准参考均取自 C++ 标准草案 n3337


简介

该标准规定,capture 必须是

&
=
this
identifier 或前面带有 &
identifier

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 表达式,就是后缀表达式。计算点或箭头之前的后缀表达式;66 该计算的结果与 id 表达式一起确定整个后缀表达式的结果。


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

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 的捕获列表中的 reference R 的名称将使 lambda 包含对 R 所指的引用,而这不是确实如此,如此片段所示。

© www.soinside.com 2019 - 2024. All rights reserved.