递归lambda和捕获(segfault)

时间:2015-12-01 13:20:45

标签: c++ c++11 lambda

编译器

g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010

Snippet 1 &捕获)

#include <functional>

int main()
{
  std::function<void ()> func = [&] () {
    func();
  };
  return 0;
}

Snippet 2 func捕获)

#include <functional>

int main()
{
  std::function<void ()> func = [func] () {
    func();
  };
  return 0;
}

两个片段都编译但是为什么运行第二个会导致分段错误?

1 个答案:

答案 0 :(得分:4)

在构建std::function之前进行捕获。

因此,您捕获std::function<void()> func的未初始化(甚至不是默认构造!)副本。所有std::function的捕获本身就是UB(在构造变量之前复制变量!),并且调用它将是偶数&#34;更多UB&#34; (调用非构造的std::function的副本!)。

参考案例捕获对func的引用,即使在初始化之前,它也是允许的,只要它只在初始化时使用。

参考案例的缺点是lambda仅在func范围内有效。 func超出范围后,它的副本也无效。根据我的经验,这很糟糕。

做一个真实的,充满力量的&#34;递归lambda,你需要像y-combinator这样的东西。

这是简短的C ++ 14 y-combinator:

template<class F>
auto y_combinate( F&& f ) {
  return [f = std::forward<F>(f)](auto&&...args) {
    return f(f, decltype(args)(args)...);
  };
}

你传递一个lambda,它希望引用自己作为第一个参数:

std::function<void ()> func = y_combinate( [](auto&& self) {
    self(self);
  }
);

它完成其余的工作。

y-combinator要求是因为您无法访问lambda正文中自己的this。所以我们加一个。

上面的y-combinator只有90%,因为它没有处理完全传递的函数对象的r / l值和常数。但它大部分时间都会服务。

这是一个稍好的y组合:

template<class F>
struct y_combinate_t {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args)const {
    return f(*this, std::forward<Args>(args)...);
  }
};
template<class F>
y_combinate_t<std::decay_t<F>> y_combinate( F&& f ) {
  return {std::forward<F>(f)};
}

使用得更好:

std::function<void ()> func = y_combinate( [](auto&& self) {
    self();
  }
);

现在传入的self在调用时不必传递self