编译器
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;
}
两个片段都编译但是为什么运行第二个会导致分段错误?
答案 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
。