【问题标题】:Lambda capture list: capturing object's member field by value not possible without capturing the whole object?Lambda捕获列表:如果不捕获整个对象,就不可能按值捕获对象的成员字段?
【发布时间】:2014-08-04 22:36:59
【问题描述】:

以下代码

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】:

注意:本文中的所有标准引用均来自 C++ 标准草案n3337


简介

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

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

capture-list:
  capture ..._opt
  capture-list , capture ..._opt

capture:
  identifier
  & identifier
  this

因为parentItem.displayName 不是标识符,而是“类成员访问表达式”。拒绝你的 sn-p 时编译器是正确的。

5.2.5p1 班级成员访问权限 [expr.ref]

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


要是有办法用表达式初始化捕获就好了……

C++14 中,您 能够使用 init-capture 来规避手头的问题,就像 sn -p 下面。它将创建一个名为 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`


注意:您的原始帖子似乎暗示 reference R 在 lambda 的捕获中的名称- list 将使 lambda 包含对 R 所指内容的引用,这是不正确的,正如 this snippet 所见。

【讨论】:

  • 所以即使 identifier 有引用类型,它仍然会捕获它通过值引用的底层对象,只要这个 identifier 不是在捕获列表中以& 开头?
  • @VioletGiraffe 完全正确。
  • 感谢您抽出宝贵时间撰写详细的回复,并附上所有解释。
  • @VioletGiraffe 这是我知道的唯一回答问题的方法,我很高兴它受到赞赏!
  • 或者,您也可以选择std::bind([](QString lmb_display_name]{ ... }, parentItem.displayName);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-31
  • 2013-12-15
  • 2012-10-08
  • 2012-12-03
  • 1970-01-01
相关资源
最近更新 更多