簡體   English   中英

遞歸lambda和捕獲(segfault)

[英]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;
}

小片2func捕獲)

#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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM