gpt4 book ai didi

c++ - 在 C++11 lambda 中按引用捕获引用

转载 作者:IT老高 更新时间:2023-10-28 13:22:49 33 4
gpt4 key购买 nike

考虑一下:

#include <functional>
#include <iostream>

std::function<void()> make_function(int& x) {
return [&]{ std::cout << x << std::endl; };
}

int main() {
int i = 3;
auto f = make_function(i);
i = 5;
f();
}

这个程序是否保证输出 5 而不会调用未定义的行为?

我了解按值 ([=]) 捕获 x 的工作原理,但我不确定我是否通过引用捕获它来调用未定义的行为.是不是在 make_function 返回后我会得到一个悬空引用,或者只要最初引用的对象仍然存在,捕获的引用就可以保证工作?

在这里寻找基于标准的明确答案 :) 它在实践中运行良好到目前为止 ;)

最佳答案

代码保证可以工作。

在我们深入研究标准措辞之前:C++ 委员会的意图是该代码有效。然而,目前的措辞被认为不够明确(事实上,对标准后 C++14 的错误修复破坏了使其工作的微妙安排),所以 CWG issue 2011被提出来澄清问题,现在正在通过委员会。据我所知,没有任何实现会出错。


我想澄清几件事,因为 Ben Voigt 的回答包含一些会造成混淆的事实错误:

  1. “作用域”是 C++ 中的静态词汇概念,它描述了程序源代码的一个区域,其中非限定名称查找将特定名称与声明相关联。它与生命无关。见 [basic.scope.declarative]/1 .
  2. 同样,lambda 的“到达范围”规则是一个确定何时允许捕获的语法属性。例如:

    void f(int n) {
    struct A {
    void g() { // reaching scope of lambda starts here
    [&] { int k = n; };
    // ...

    n 在这里是在作用域内,但是 lambda 的到达作用域不包括它,所以不能被捕获。换句话说,lambda 的到达范围是它可以到达和捕获变量的“向上”距离——它可以到达封闭的(非 lambda)函数及其参数,但它不能到达那个范围之外并且捕获出现在外面的声明。

所以“达到范围”的概念与这个问题无关。被捕获的实体是make_function的参数x,在lambda的可达范围内。


好的,让我们看看标准在这个问题上的措辞。根据 [expr.prim.lambda]/17,只有引用由拷贝捕获的实体的 id-expression 被转换为对 lambda 闭包类型的成员访问; id-expressions 引用通过引用捕获的实体被单独保留,并且仍然表示它们在封闭范围中表示的同一实体。

这看起来很糟糕:引用 x 的生命周期已经结束,那么我们如何引用它呢?好吧,事实证明几乎(见下文)没有办法在其生命周期之外引用引用(您可以看到它的声明,在这种情况下它在范围内,因此可以使用,或者它是一个类成员,在这种情况下,类本身必须在其生命周期内才能使成员访问表达式有效)。因此,该标准直到最近才禁止在其生命周期之外使用引用。

lambda 措辞利用了这样一个事实,即在其生命周期之外使用引用不会受到惩罚,因此不需要为通过引用捕获的实体的访问方式给出任何明确的规则——它只是意味着您使用该实体;如果是引用,则名称表示它的初始化器。这就是保证直到最近(包括在 C++11 和 C++14 中)才能正常工作的方式。

但是,您不能在其生命周期之外提及引用,这不是完全正确的;特别是,您可以从它自己的初始化程序中引用它,从比引用更早的类成员的初始化程序中引用它,或者如果它是一个命名空间范围的变量并且您从另一个在它之前初始化的全局变量中访问它。 CWG issue 2012被引入来解决这个疏忽,但它无意中通过引用引用破坏了 lambda 捕获的规范。我们应该在 C++17 发布之前修复这个回归;我已经提交了国家机构的评论,以确保它得到适当的优先级。

关于c++ - 在 C++11 lambda 中按引用捕获引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21443023/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com