c++ - 右值 lambda 的 std::reference_wrapper 如何工作?

标签 c++ c++11 lambda

this article它说以下代码是有效的 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() 是否真的可以用来绑定(bind)右值。根据 5.1.2 [expr.prim.lambda] 第 2 段,lambda 表达式显然是临时的(感谢 DyP 提供引用)。由于将引用绑定(bind)到临时对象通常是一个相当糟糕的主意并且在其他地方被禁止,因此 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;

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

最后,上面的表达式中没有任何东西可以延长临时对象的生命周期。临时对象生命周期延长的唯一原因是当它或其中一个数据成员立即绑定(bind)到 [const] 引用时。围绕临时对象包装函数调用会抑制此生命周期扩展。

总结:文中引用的代码在很多不同的层面上都是不合法的!

关于c++ - 右值 lambda 的 std::reference_wrapper 如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20305723/

相关文章:

c++ - Boost Geometry 和精确的点类型

c++ - Log4cxx 自定义附加器

c++ - 将 std::array 转换为 std::vector

c++ - Address-of(&) 运算符传递的无效指针

c++ - g++ 中的 fstream 链接错误与 -std=gnu++0x

java - Java 的 lambda 语法的分割是什么?

c++ - 将整数数组传递给函数时计算元素数量时得到不同的结果

c++ - 在 C++ 项目中使用基于 CUDA 的库代码

c++ - Visual Studio 2012 中的 std::function 目标方法

c# - 将 lambda 重构为事件方法