2016-05-31 38 views
0

我剛接觸cpp並試圖使用lambda函數。我有一個計數器,我試圖在lambda函數內增加一個內部值。我看到一些我無法理解的奇怪記憶錯誤。這是我與這個櫃檯合作的流程。有什麼我做錯了嗎?C++ Lambda函數關閉 - 內存問題

bool SomeClass::func() { 
    int64_t counter = 0; 

    //some loop logic { 
     counter++; 
    } 


    auto lamb = [this, &counter]() { 
     //some logic 
     counter++; 
    } 

    someotherFunction(data, lamb); // this function will execute the lambda 

} 
+3

由於該函數被寫入,它應該工作 - 因爲如果在'someotherFunction'內執行'lamb',那麼'counter'仍然在範圍內。但是,如果'someotherFunction' *存儲* lambda,並且稍後在'func'返回後,lambda被執行,那麼你有一個懸而未決的引用問題。 –

+0

但看看下面的評論,似乎如果其他功能的執行超過func執行,那麼它會失敗嗎? – sublime

+0

關鍵是你從* func中*調用'someotherFunction',這意味着它*不能*超過'func'。 –

回答

7

的問題是,當一個局部變量是通過參考由生存的地方的「所有者」(因爲它是存儲在某處的λ捕獲C++ lambda表達式不能夠解決「upwards funarg」的情況下,即或者因爲它作爲函數結果返回)。

當lambda通過引用捕獲變量時,C++所做的只是將引用變量的地址存儲在由lambda代碼使用的上下文結構中。

然而,在你的情況下,變量本身是一個本地和生活在lambda創建的堆棧幀。如果在執行someOtherFunction期間剛剛調用了lambda對象,那麼情況很好,但是如果lambda是而不是,並且存活了創建它的堆棧幀(或者更準確地說,創建了在lambda中引用的捕獲的變量),當它被執行時會引用一個不再存在的變量(未定義的行爲)。

在一般情況下解決「向上funarg」問題需要一個垃圾回收器,並且C++沒有一個。

,你可以在某些情況下,做的是「按價值」捕捉,所以拉姆達將擁有自己的私人副本:

foo([counter]() mutable { counter++; }) 

在但是如果你想改變捕獲這種情況下複製,還需要使用mutable關鍵字是因爲......好吧,只是因爲如果你想修改捕獲的副本(捕獲的副本是lambda主體中的其他const對象),這就是C++所要求的。

不幸的是,如果您需要共享捕獲的變量,例如使用兩個lambda表達式(例如,在同一捕獲的變量上創建「增量」和「減法器」),則使用副本是不可行的。 你可以做的是爲變量捕獲值std::shared_ptr,這將正確替換簡單情況下的垃圾回收器(不過在參考循環的情況下)。

+0

我該如何解決這個問題?有沒有辦法通過值的局部變量傳遞給lambda呢? – sublime

+0

當你提到要使用共享指針時,你是不是要說:'auto pin = std :: make_shared (counter);'並通過值捕獲'pin'? @ 6502 – Anzurio

+1

@Anzurio:是的。如果lambda生命週期本身不是由共享指針處理的,而這些指針可能會與捕獲的對象創建一個引用循環(C++不提供垃圾收集器,所以被放棄的引用循環是泄漏),則通過值捕獲'shared_ptr'是可以的。 – 6502

0

添加一個切實可行的辦法,以6502的出色答卷:

auto make_counter(int initial) { 
    return [initial] (void) mutable { 
    return ++initial; 
    }; 
} 

這裏,局部變量(實際上它是一個函數的參數,但是這重要的不是)由價值捕獲,這意味着有一個副本「裏面「的lambda的每個對象。爲了能夠增加計數器,lambda必須允許修改其捕獲的變量,因此mutable