Lambda通过引用捕获右值引用

时间:2019-04-07 13:04:49

标签: c++ lambda temporary-objects forwarding-reference

以下代码标准正确吗? (godbolt

即by-ref捕获表示临时引用的转发引用,并从函数在同一表达式中返回所得的lambda按值。

当然存储lambda供以后使用会使它包含一个悬挂的引用,但是我指的是main中的确切用法。

我对this SO answer以及可能对this language defect的怀疑。具体来说,有一条令人生畏的评论说:“ “标准引用中的引用捕获生存期规则是捕获变量,而不是数据及其范围”。 –这似乎表示捕获到的临时引用可能无效。我的代码。

#include <stdlib.h>
#include <string.h>
#include <cassert>

template<typename F>
auto invoke(F&& f)
{
    return f();
}

template<typename F>
auto wrap(F&& f)
{
    return [&f]() {return f();}; // <- this by-ref capture here
}

int main()
{
    int t = invoke(wrap(
        []() {return 17;}
    ));

    assert(t == 17);
    return t;
}

2 个答案:

答案 0 :(得分:2)

是的,您的代码中没有UB。 f已绑定到lambda,但是您调用了在同一表达式中捕获f的lambda,因此其生存期尚未结束。您链接的缺陷报告阐明了通过参考方式捕获参考的内容。通过澄清引用捕获实际上是对该捕获引用所绑定到的对象的引用来解决。

在您的情况下,捕获的f是对lambda(而不是参数f)的引用。

答案 1 :(得分:2)

您的代码中存在UB,因此窗口相对较短。原始的lambda按引用捕获规则指出,该引用仅在捕获的变量超出范围之前有效。

这可能会导致按引用进行捕获,否则在C ++标准中是不可能的。 (您可以获得的最接近的引用将是对包含引用的单成员结构的引用)

从理论上讲,您可以利用这一事实使lambda参考捕获基于堆栈框架;捕获当前堆栈帧,并且所有(几乎?)按引用参数都将相对于该堆栈帧具有固定的偏移量。

由于大多数(全部?)ABI在后台将引用参数实现为指针,这将导致函数参数的引用参数成为在lambda返回之后悬挂的引用。

没有编译器利用这一事实。从未使用过这种优化,只是尽可能地观察到了。 “ lambda的引用捕获具有变量引用的生存期”规则从未被任何编译器(或至少我听说过的任何一个)利用。

发现它后,它已作为标准中的缺陷解决方案解决,这意味着它可追溯地重新定义的含义。

因此,在历史上编译器下,从技术上讲,它是UB,没有当前兼容的编译器可以将其视为UB,并且所有历史C ++ 11编译器都将其与当前编译器相同。所以你很安全。