c++ Lambda逐引用捕获右值引用

nxagd54h  于 2022-06-30  发布在  其他
关注(0)|答案(2)|浏览(233)

以下代码是否符合标准?(godbolt
例如,by-ref捕获表示临时的转发引用,并从函数返回结果lambda by-value,在同一表达式中。
当然,存储lambda供以后使用会使它包含一个悬空引用,但我指的是main内部的确切用法。
我的疑问与this 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;
}
vc9ivgsu

vc9ivgsu1#

您的代码中存在UB,用于相对较短的历史时间窗口。(注意:这是一个非常奇怪的说法).原始的lambda引用捕获规则声明引用只有在捕获的 * 变量 * 超出范围时才有效。
在当前标准和每个追溯修订的过去标准下,您的代码中没有UB。
这可能导致一种逐引用捕获,否则在C++标准中是不可能的。(最接近的情况是对包含引用的单成员结构的引用)
理论上,您可以利用这个事实使lambda引用捕获基于堆栈框架;捕获当前的堆栈帧,并且所有(几乎是)按引用的参数都将位于该堆栈帧的固定偏移量处。
由于大多数(所有?)ABI都将引用参数实现为幕后的指针,这将导致函数参数的引用参数是在返回的lambda之后悬挂的引用。

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

当它被发现时,它在标准中被解决为缺陷解决方案,这意味着它追溯性地重新定义了c++11的含义。
因此,虽然在历史上的c++11编译器中,这在技术上是UB,但当前兼容的c++11编译器都不能将其视为UB,并且所有历史上的C++11编译器都以当前编译器的方式处理它。

0ve6wy6x

0ve6wy6x2#

是的,你的代码中没有UB。f绑定到lambda,但是你调用了lambda,它在同一个表达式中捕获f,所以它的生存期还没有结束。你链接的缺陷报告澄清了通过引用捕获引用的含义。通过澄清引用捕获实际上是对捕获的引用绑定到的对象的引用,解决了这个问题。
在您的示例中,捕获的f是对lambda的引用(而不是对参数f的引用)。

相关问题