繁体   English   中英

右值Lambda的std :: reference_wrapper如何工作?

[英]How can a std::reference_wrapper to a rvalue lambda work?

本文中,它说以下代码是有效的C ++ 11,并且可以与GNU的libstdc ++一起使用:

int n;
std::vector<int> v;
...
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
std::count_if(v.begin(), v.end(), f);

事实是,我一直相信将在调用站点上创建lambda对象,这使它成为此代码段中的临时对象,因为它没有存储在任何变量中,而是创建并传递了对其的const引用。到std::function 如果是这样,则应始终销毁lambda对象,在f内留下一个悬空的引用,当std::count_if使用该引用时会导致未定义的行为。

假设这篇文章没有错,我的心理模型有什么问题? Lambda对象何时被破坏?

好的,让我们从基础开始:上面的代码当然是不合法的,因为它以某种相当基本的方式格式不正确。 线

std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));

至少需要写成

std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0;}));

请注意,代码是在问题中直接写在Dr.Dobb的文章中的,也就是说,任何关于代码合法的陈述都已经很成问题了。

解决了简单的语法错误后,下一个问题是std::cref()是否可以实际用于绑定到右值。 根据5.1.2 [expr.prim.lambda]第2段,lambda明显是暂时的(感谢DyP作为参考)。 由于将引用绑定到临时对象通常是一个非常糟糕的主意,并且在其他地方被禁止,因此std::cref()将是规避此限制的一种方法。 事实证明,根据20.10 [function.objects]第2段, std::cref()被声明为

template <class T> reference_wrapper<const T> cref(const T&) noexcept;
template <class T> void cref(const T&&) = delete;
template <class T> reference_wrapper<const T> cref(reference_wrapper<T>) noexcept;

即,即使更正了语法错误,该语句也不正确。 gccclang都不会编译此代码(我已经使用了两个编译器的最新版本及其各自的标准C ++库)。 也就是说,根据以上声明,此代码显然是非法的!

最后,在上面的表达式中没有什么可以延长临时项的寿命。 临时对象的生存期被延长的唯一原因是当其或其数据成员之一立即绑定到[ const ]引用时。 将函数调用包装在临时变量周围会禁止此生命周期扩展。

总结:本文引用的代码在许多不同级别都是不合法的!

我是上述文章的作者,对于我的错误我深表歉意 在这种情况下,无人可责。 我只是要求编辑器添加勘误表:

1)更换

std::count_if(v.begin(), v.end(), std::cref(is_multiple_of(n)));

is_multiple_of f(n);
std::count_if(v.begin(), v.end(), std::cref(f));

2)更换

std::count_if(v.begin(), v.end(), std::cref([n](int i){return i%n == 0;}));

auto f([n](int i){return i%n == 0;});
std::count_if(v.begin(), v.end(), std::cref(f));

3)更换

std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));

auto f1([n](int i){return i%n == 0;});
std::function<bool(int)> f(std::cref(f1));

在所有情况下,问题都是相同的(正如DietmarKühl很好地解释过的,对他+1):我们正在临时调用std::cref 此函数返回一个std::reference_wrapper ,该指针存储指向该临时对象的指针,如果std::reference_wrapper寿命超过该临时对象,则该指针将悬空。 基本上,这就是上述情况3(其中也包含错字)的情况。

在情况1和2中, std::reference_wrapper不会超过临时目录。 但是,由于删除了接受临时对象(rvalues)的std::cref重载,因此不应编译代码(包括情况3)。 在发布之时,这些实现还没有与标准保持一致。 该代码曾经用于编译,但是在与标准库的较新实现一起使用时未使用。 这不是我犯错的借口。

无论如何,我相信本文的要点就是使用std::reference_wrapperstd::crefstd::ref来避免昂贵的复制和动态分配仍然是有效的,当然,引用对象的生命周期足够长。

再次造成不便,我深表歉意。

更新:文章已修复。 感谢uk4321,DyP,尤其是lvella和DietmarKühl提出并讨论了这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM