6

下面的代码标准正确吗?(神螺栓

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

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

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

#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;
}
4

2 回答 2

7

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

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

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

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

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

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

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

于 2019-04-07T14:38:59.407 回答
2

是的,您的代码中没有 UB。f绑定到 lambda,但是您调用了f在同一个表达式中捕获的 lambda,因此它的生命周期还没有结束。您链接的缺陷报告阐明了通过引用捕获引用的方式。通过澄清引用捕获实际上是对捕获的引用绑定到的对象的引用来解决这个问题。

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

于 2019-04-07T13:12:35.880 回答