【问题标题】:Lambda capture reference variable by referenceLambda 通过引用捕获引用变量
【发布时间】:2014-04-14 18:08:54
【问题描述】:

对于 lambda,我想通过引用捕获一些已经通过引用保存在外部作用域中的东西。假设引用的值超过 lambda,但不是创建 lamdba 的范围。

I know that 如果 lambda 按值 捕获引用变量,则将复制引用的对象。我想避免这个副本。

但是如果我通过引用捕获一个引用变量会发生什么?如果原始引用变量在执行 lambda 之前超出范围怎么办?这安全吗?换句话说:是在引用后面的object被引用还是在lambda中引用了引用变量

auto f() {
    const auto & myRef = g();
    return [&]{ myRef.doSomething(); };
}

f()();  // Safe?

【问题讨论】:

  • 我不相信。除了理论上,实现[&] 的一种有效方法是存储堆栈深度,并在 lambda 主体中硬编码偏移量。这会将您的状态减少到一个指针,而不是每个变量一个。至于这是一个合法的实施,我想我在其他地方看到了关于 SO 的论点。哦,注意参考延长寿命的危险。作为解决方案,按值捕获引用包装器?
  • @dyp,假设 g 返回对长期对象的引用。如 OP 中所述。
  • 另一种解决方法:auto myRef = std::cref(g()); return [=]{ myRef.get().doSomething(); }
  • @leemes [&myRef = myRef] 应该没问题。这会在闭包中创建一个引用数据成员。 [myRef = *&myRef] 执行复制,就像 [myRef = myRef]

标签: c++ c++11 lambda


【解决方案1】:

是的,通过引用捕获对象的关键问题是被引用对象的生命周期,而不是用于获取它的任何中间引用的生命周期。您可以将引用视为别名而不是实际变量。 (并且在类型系统中,引用的处理方式与常规变量不同。)引用为原始对象起别名,并且独立于用于为对象起别名的其他别名(除了它们为同一个对象起别名的事实)。

=====编辑=====

根据this SO question 给出的答案(由dyp 指出),看来这可能并不完全清楚。在整个语言的其余部分,“对引用的引用”的概念没有意义,并且从引用创建的引用成为该引用的对等,但显然标准对这种情况有些模棱两可,并且被 lambda 捕获引用在某种意义上可能是次要的,取决于从中捕获它们的堆栈帧。 (SO 答案引用的明确措辞明确指出了引用的实体,这从表面上表明,只要原始对象存在,这种用法就是安全的,但绑定机制可能暗示捕获链很重要。)

我希望在 C++14/17 中对此进行澄清,并且我希望对其进行澄清以保证这种用法的合法性。特别是,我认为 C++14/17 通过表达式捕获变量的能力将使通过堆栈帧指针简单地捕获范围变得更加困难,而最明智的捕获机制通常是捕获特定实体分别。 (如果通过引用捕获实际的本地对象,则可能允许堆栈帧捕获,因为如果无论如何在范围之外调用 lambda,这将导致 UB。)

在我们得到一些澄清之前,这可能是不可移植的。

【讨论】:

  • 谢谢,我有 99% 的把握,但需要一些确认。
  • 通过引用捕获不会(必然)创建引用。
  • 根据 Yakk 对我的问题的评论,您的回答不正确。 :(
  • 除了本例中的“引用实体”是函数局部引用myRef,而不是对象。
  • lambda 中没有引用(不一定),因此没有对引用的引用。 “通过引用捕获”是一个相当具有历史意义的术语,因为早期版本的 lambda 为闭包类型指定了引用类型的数据成员。这在 C++11 之前已被删除,现在只有 对实体的访问 在 lambda 内指定(或者更确切地说,没有转换到另一个实体,因此外部范围的原始实体是访问)。而这里的实体是一个声明为引用的变量。
猜你喜欢
  • 2019-08-28
  • 2011-09-25
  • 1970-01-01
  • 2023-03-03
  • 2016-02-08
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
  • 2020-01-23
相关资源
最近更新 更多