2011-12-19 24 views
8

我一直在玩C++ 11中的一些新功能,並試圖編寫下面的程序,期望它不起作用。出乎我的意料,它(海合會4.6.1在Linux x86與「STD = C++ 0x」爲標誌):C++ Lambda,捕獲,智能Ptrs和堆棧:爲什麼這樣工作?

#include <functional> 
#include <iostream> 
#include <memory> 

std::function<int()> count_up_in_2s(const int from) { 
    std::shared_ptr<int> from_ref(new int(from)); 
    return [from_ref]() { return *from_ref += 2; }; 
} 

int main() { 
    auto iter_1 = count_up_in_2s(5); 
    auto iter_2 = count_up_in_2s(10); 

    for (size_t i = 1; i <= 10; i++) 
     std::cout << iter_1() << '\t' << iter_2() << '\n' 
     ; 
} 

我期待「from_ref」被刪除時的每一次執行返回的lambda運行。這是我的推理:一旦運行了count_up_in_2s,from_ref就會彈出堆棧,但是因爲返回的lambda不是直接運行的,因爲它返回了,所以在一段短暫的時間內沒有另一個引用,直到相同的引用爲止當lambda實際運行時推回,所以不應該shared_ptr的引用計數爲零,然後刪除數據?

除非C++ 11的lambda捕獲比我給它的功勞更聰明,如果是這樣,我會很高興。如果是這種情況,只要/ something /正在處理動態分配的內存,我是否可以假設C++ 11的變量捕獲將允許所有的詞法範圍/封閉技巧和la Lisp?我能否假設所有捕獲的引用都會一直存在,直到lambda本身被刪除,從而允許我以上述方式使用smart_ptrs?

如果這是因爲我認爲是這樣,這是否意味着C++ 11允許表達式高階編程?如果是這樣,我認爲C++ 11委員會做了出色的工作=)

回答

7

該lambda按值捕獲from_ref,所以它作出副本。由於這個副本,當from_ref被破壞時,ref計數不爲0,因爲仍存在於lambda中的副本,所以它是1。

+1

所以我是對的理解是,性病::函數對象本身在整個實例的持續時間存儲捕捉值

你的推理作品?而通過存儲這個引用,shared_ptr引用計數永遠不會達到0?啊,我明白了。多麼優雅。 – Louis 2011-12-19 04:19:33

+3

@Louis不,不是函數對象,而是lambda。 'std :: function'不知道lambdas的捕獲。 – 2011-12-19 04:23:14

+0

因此,捕獲和存儲引用被視爲一種特殊情況,而不是作爲std :: function實例中的成員存儲,比如說。謝謝。 – Louis 2011-12-19 04:24:05

4

以下:

std::shared_ptr<int> from_ref(new int(from)); 
return [from_ref]() { return *from_ref += 2; }; 

大多是等效於這樣的:

std::shared_ptr<int> from_ref(new int(from)); 
class __uniqueLambdaType1432 { 
    std::shared_ptr<int> capture1; 
public:   
    __uniqueLambdaType1432(std::shared_ptr<int> capture1) : 
    capture1(capture1) { 
    } 
    decltype(*capture1 += 2) operator()() const { 
    return *capture1 += 2; 
    } 
}; 
return __uniqueLambdaType1432(from_ref); 

其中__uniqueLambdaType1432是從由詞法相同產生的任何其它,即使是其他的λ類型的程序,全局唯一的類型不同lambda表達式。它的實際名稱不適用於程序員,除了原始lambda表達式產生的對象之外,不能創建其他實例,因爲構造函數實際上隱藏着編譯器魔術。

1

from_ref被值捕獲。如果替代

return [from_ref]() { return *from_ref += 2; }; 

return [&from_ref]() { return *from_ref += 2; };