2012-07-22 100 views
24

下面的代碼打印0,但我期望看到一個1.我的結論是lambda函數不會通過實際將捕獲的參數傳遞給函數來調用,直觀。我是對的還是缺少什​​麼?C++ 11通過值捕獲的lambda捕獲在聲明點

#include <iostream> 
int main(int argc, char **argv){ 
    int value = 0; 
    auto incr_value = [&value]() { value++; }; 
    auto print_value = [ value]() { std::cout << value << std::endl; }; 
    incr_value(); 
    print_value(); 
    return 0; 
} 

回答

23

lambda函數通過實際傳遞捕獲參數的函數調用。

value在定義λ的點處等於0(並捕獲value)。由於您按價值進行捕獲,因此捕獲後您對value所做的操作無關緊要。

如果您通過引用捕獲了value,那麼您將看到打印1,因爲即使捕獲點仍然相同(lambda定義),您將打印捕獲的對象的當前值而不是副本它在被捕獲時創建。

+0

謝謝。我一直在思考價值,因爲價值的更新在功能之外是不可見的。但是,這聽起來像也意味着函數外部的更新在函數內部不可見。 – perreal 2012-07-22 10:49:30

+6

這裏可能會誤導的是措辭:按價值意思是「生成副本」。 lambda具有變量的副本,它是在lambda的聲明處獲取的值。由於lambda具有私人副本,因此原始對象不會在lambda內部修改或讀取。這就是爲什麼實際上有一個通過引用被捕獲的原因,以允許你想要查看/修改原始的情況。 – Klaim 2012-07-22 11:56:06

12

是的,捕獲是在聲明lambda的時候完成的,而不是在它被調用時完成的。把lambda看作一個函數對象,它的構造函數將捕獲的變量作爲參數,並將它們分配給其相應的成員變量(值或引用,取決於捕獲模式).Lambda的實際調用沒有魔力,它只是一個定期調用底層函數對象operator()

在調用點捕獲事物沒有什麼意義 - 如果lambda返回或作爲參數傳遞給另一個函數並在那裏調用,會捕獲什麼?實際上有這樣的語言 - 如果你在一個函數中引用一個變量x,則假定它指向當前處於調用點範圍內的任何名爲x的變量。這被稱爲動態範圍界定。這種替代方法被大多數語言使用,因爲它使程序的推理更加簡單,稱爲詞法範圍界定。

http://en.wikipedia.org/wiki/Lexical_scoping#Lexical_scoping_and_dynamic_scoping

5

的問題是,您的打印功能是按值而不是通過引用捕捉。

#include <iostream> 
int main(int argc, char **argv){ 
    int value = 0; 
    auto incr_value = [&value]() { value++; }; 
    auto print_value = [ value]() { std::cout << value << std::endl; }; 
    auto print_valueref = [ &value]() { std::cout << value << std::endl; }; 

    incr_value(); 
    print_value(); 
    print_valueref(); 
    return 0; 
} 

按預期輸出0和1。第一個按值捕獲並在捕獲點打印值;第二個捕獲參考,然後打印它的值。