2014-09-23 147 views
8

我創造了我的λ像這樣:「刷新」拉姆達對象

int i = 0; 
auto gen_lam = [=]() mutable -> int {return ++i;}; 

,有效計算的時候,它被稱爲數字,因爲它存儲所拍攝的i。有沒有辦法「重建」對象,使其初始值爲i

線沿線的東西:

decltype(gen_lam) gen_lam2; 

,使得下面的代碼輸出1 1代替1 2

std::cout << gen_lam() << std::endl; 

decltype(gen_lam) gen_lam2; 

std::cout << gen_lam2() << std::endl; 
+3

爲了簡單和完整,我會爲此創建一個普通的舊仿函數類。 – 2014-09-23 13:43:34

+0

我想將它存儲在'std :: function'中,並且不想在我的類的調用者/用戶上強加另一種類型 – TeaOverflow 2014-09-23 13:50:50

+0

@TeaOverflow:只要類型擦除的額外開銷是可接受的... – Deduplicator 2014-09-23 13:51:52

回答

17

簡單地完成,包裝在一個lambda,你可以打電話給你的λ-創作無論何時您需要重新初始化內部拉姆達:

auto wrap_lam = [](int i) {return [=]() mutable {return ++i;};} 
auto gen_lam = wrap_lam(0); 
auto gen_lam2 = wrap_lam(0); 

或者只是做一個副本保存狀態,只要你想:

auto gen_lam = [=]() mutable -> int {return ++i;}; 
const auto save = gen_lam; 

如果您只想完全復位,自然保存爲一個const對象第一。

+4

你甚至可以編寫'auto const prototype = [=]()mutable {return ++ i; };所以你知道原型本身並不是偶然增加的。 – MSalters 2014-09-23 13:57:51

+0

不錯......我甚至沒有想過在發生器中捕獲傳值計數器。 – IdeaHat 2014-09-23 14:00:39

+0

@ MSalters:只是想表明,它可以在任何時候想要保存,而不僅僅是在創作時。當然,如果只需要完全復位,那麼由於編譯器會抱怨邏輯錯誤的語法錯誤,所以最好使用你的方法。 – Deduplicator 2014-09-23 14:04:05

1

我不認爲由lambda捕獲的初始值可以是其類型的一部分,因爲這將意味着每次代碼運行(可能具有不同的值i),新類型必須是在運行時生成。

我也不認爲存儲捕獲的可變值的初始狀態,因爲這會佔用額外的內存。所以總而言之,如果你所擁有的只是一個按你描述的方式創建的lambda函數對象,我認爲你沒有辦法重新創建它的原始狀態。但是,如果您控制生成該函數對象的代碼,則可以將其拉入函數中,然後再次調用該函數。或者你可以手動創建一個函數對象類,就像@NeilKirk已經提出的那樣。

+0

捕獲的變量的初始值不需要存儲,因爲原始的'i'被值捕獲並因此沒有「觸及」 – TeaOverflow 2014-09-24 14:20:09

+0

是真的,但原始的'i'可能早已超出範圍。在你的問題中,它不是很清楚爲什麼你想刷新/重置lambda對象,所以我嘗試用最小的假設來解決問題 - 只給出* lambda對象,嘗試重置它或創建一個「刷新「複製。 – Medo42 2014-09-24 16:09:19

+0

在您的代碼中,會有兩個版本的'i':一個是局部變量(始終保持不變),另一個是lambda中的值副本。但是,每次調用lambda時,該副本都會被修改,所以您必須回到原來的「i」才能獲取起始值,您只能在lambda對象本身查看* only *來獲取它。 – Medo42 2014-09-24 16:14:41

2

我想你可以通過捕獲不同計數器的發生器來完成。

#include <iostream> 
#include <functional> 

using namespace std; 

int main() 
{ 
    int i = 0; 
    int j = 0; 

    auto lambda_generator = [](int& val) { 
     auto var = [=]() mutable -> int {return ++val;}; 
     return var; 
    }; 

    auto counter1 = lambda_generator(i); 

    std::cout << counter1() << std::endl; 

    auto counter2 = lambda_generator(j); 

    std::cout << counter2() << std::endl; 

} 

如果「重置」表示您想要一個不同的計數器。

+0

第二個例子的共享狀態讓我感到困惑。每個lambda具有自己的狀態的Deduplicator解決方案要好得多。 – 2014-09-23 13:56:53

+0

@SebastianRedl我同意......我甚至沒有想到捕獲複製的傳值整數。爲了教學目的,我會在這裏留下糟糕的答案。 – IdeaHat 2014-09-23 14:00:10

+0

@MSalters我會完全刪除第二個例子 – IdeaHat 2014-09-23 14:05:30

2

您不能訪問在lambda內複製的內部變量,但lambda本身可以。

通過在呼叫上建立一個協議,你可以發送命令給lambda來改變它的狀態。例如:

auto counter = [](int i0){ 
    int i = i0; 
    return [i0, i](bool reset=false) mutable { 
     if (reset) { 
      i = i0; 
      return -1; 
     } else { 
      return ++i; 
     } 
    }; 
}; 

這裏我使用一個可選的參數傳遞時true將計數器復位到初始值。

int main() { 
    auto c = counter(10); 
    std::cout << c() << "\n"; // 11 
    std::cout << c() << "\n"; // 12 
    std::cout << c() << "\n"; // 13 
    c(true); // send reset message 
    std::cout << c() << "\n"; // 11 
    std::cout << c() << "\n"; // 12 
    std::cout << c() << "\n"; // 13 
}