[英]recursive lambda and capture (segfault)
編譯器
g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
小片1 ( &
捕獲)
#include <functional>
int main()
{
std::function<void ()> func = [&] () {
func();
};
return 0;
}
小片2 ( func
捕獲)
#include <functional>
int main()
{
std::function<void ()> func = [func] () {
func();
};
return 0;
}
兩個片段都編譯但是為什么運行第二個片段會導致分段錯誤?
捕獲發生在構造std::function
。
因此,您捕獲std::function<void()> func
的未初始化(甚至不是默認構造!)副本。 一直都是std::function
的捕獲是UB(在構造之前復制變量!),並且調用它甚至會“更多UB”(調用非構造的std::function
的副本! )。
參考案例捕獲對func
的引用,即使在初始化之前它也被允許,只要它只在初始化時使用。
參考案例的缺點是lambda僅在func
范圍內保持有效。 一旦func
超出范圍,它的副本也無效。 根據我的經驗,這很糟糕。
要做一個真正的“全力”遞歸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組合子的要求是因為你無法在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
調用時本身。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.