Lambda 通过引用捕获右值引用

hae*_*lix 6 c++ lambda temporary-objects forwarding-reference

下面的代码标准正确吗?(天马行空

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

当然,存储 lambda 以供以后使用会使它包含一个悬空引用,但我指的是main.

我的疑虑与这个 SO answer和潜在的这种语言缺陷有关。具体来说,有一个令人生畏的评论说“标准引用中的引用捕获生命周期规则捕获了变量,而不是数据及其范围” ——这似乎是说捕获的临时引用在我的代码中可能是无效的。

#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;
}
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 7

您的代码中有 UB 用于相对较短的窗口。(注意:这是一个很奇怪的说法)。原始的 lambda 引用捕获规则规定引用仅在捕获的变量超出范围之前有效。

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

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

由于大多数(所有?)ABI 将引用参数实现为引擎盖下的指针,这将导致函数参数的引用参数在 lambda 返回后悬空。

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

当它被发现时,它在标准中被解析为缺陷解决方案,这意味着它追溯重新定义了含义。

因此,虽然在历史上的编译器下,这在技术上是 UB,但没有当前兼容的编译器可以将其视为 UB,并且所有历史上的 C++11 编译器都以当前编译器的方式对待它。所以你很安全。