以下代码是否符合标准?(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;
}
2条答案
按热度按时间vc9ivgsu1#
您的代码中存在UB,用于相对较短的历史时间窗口。(注意:这是一个非常奇怪的说法).原始的lambda引用捕获规则声明引用只有在捕获的 * 变量 * 超出范围时才有效。
在当前标准和每个追溯修订的过去标准下,您的代码中没有UB。
这可能导致一种逐引用捕获,否则在C++标准中是不可能的。(最接近的情况是对包含引用的单成员结构的引用)
理论上,您可以利用这个事实使lambda引用捕获基于堆栈框架;捕获当前的堆栈帧,并且所有(几乎是)按引用的参数都将位于该堆栈帧的固定偏移量处。
由于大多数(所有?)ABI都将引用参数实现为幕后的指针,这将导致函数参数的引用参数是在返回的lambda之后悬挂的引用。
当它被发现时,它在标准中被解决为缺陷解决方案,这意味着它追溯性地重新定义了c++11的含义。
因此,虽然在历史上的c++11编译器中,这在技术上是UB,但当前兼容的c++11编译器都不能将其视为UB,并且所有历史上的C++11编译器都以当前编译器的方式处理它。
0ve6wy6x2#
是的,你的代码中没有UB。
f
绑定到lambda,但是你调用了lambda,它在同一个表达式中捕获f
,所以它的生存期还没有结束。你链接的缺陷报告澄清了通过引用捕获引用的含义。通过澄清引用捕获实际上是对捕获的引用绑定到的对象的引用,解决了这个问题。在您的示例中,捕获的
f
是对lambda的引用(而不是对参数f
的引用)。