2014-02-19 25 views
8

我今天在XCode下這種奇怪的lambdas行爲已經非常困難 - 在試圖追蹤iOS中圍繞代碼的幾處內存泄漏之後,我縮小了它的範圍到這一點(類似)的片段(S),我使用的是共享指針的東西的所有權分配給延期任務:C++ lambda將不會調用析構函數在按值捕獲的成員上

void DBStorage::dispose(std::shared_ptr<DataChunk>& dc) 
{ 
    backgroundQueue.queueTask([=]() { 
     assert(dc.use_count() == 1); 

     if (dc->isDirty()) { 
      //store to disk 
     } 
    }); 
} 

(請注意,共享指針的使用計數始終爲1,當拉姆達運行)

執行後,這個任務是空的 - 我想用pendingJob = nullptr;來調用所有按值捕獲的析構函數對象,因此是DataChunk的析構函數。 但是,它看起來像是在XCode/LLVM lc的析構函數中永遠不會被調用;使用mutable明確調用它的dtor,並用簡單的delete刪除std::function也不起作用。

這是標準行爲嗎? 我當然可以手動調用dc.reset(),它按預期工作,但這相當重要的是使用共享指針模擬。


解決方案 顯然,這是一個known gcc bug


的Contrib

單機與輸出採樣從Xcode中5.0.2 /鐺3.3

#include <iostream> 
#include <memory> 

void fnRef(std::shared_ptr<int>& ptr) 
{ 
    auto lambda = [=]() { std::cout << ptr.use_count() << ':' << __PRETTY_FUNCTION__ << '\n'; }; 
    lambda(); 
} 

void fnVal(std::shared_ptr<int> ptr) 
{ 
    auto lambda = [=]() { std::cout << ptr.use_count() << ':' << __PRETTY_FUNCTION__ << '\n'; }; 
    lambda(); 
} 

int main() 
{ 
    std::shared_ptr<int> ptr(new int); 
    for (int i=0; i<10; ++i) 
     fnVal(ptr); 
    std::cout << '\n'; 

    for (int i=0; i<10; ++i) 
     fnRef(ptr); 

    return 0; 
} 

LLVM/GCC輸出

3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 
3:void fnVal(std::shared_ptr<int>)::<anonymous class>::operator()() const 

2:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
3:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
4:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
5:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
6:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
7:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
8:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
9:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
10:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 
11:void fnRef(std::shared_ptr<int> &)::<anonymous class>::operator()() const 

IDEOne.com Output for same code

3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
3:fnVal(std::shared_ptr<int>)::__lambda1 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 
2:fnRef(std::shared_ptr<int>&)::__lambda0 

的Visual Studio 2013的輸出

3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 
3:fnVal::<lambda_67137a3f93ee478c018cc7068004c9fd>::operator() 

2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
2:fnRef::<lambda_70f241d4201227663d23c74be170d302>::operator() 
+1

您使用的是哪個版本的編譯器?海灣合作委員會存在一個錯誤,即一個參考值的捕獲是一個參考。 http://stackoverflow.com/questions/6529177/capturing-reference-variable-by-copy-in-c0x-lambda –

+0

'clang --version'說,'Apple LLVM 5.0版(clang-500.2.75)(基於在LLVM 3上。3svn)' - 這絕對是可能的,但是首先在本地範圍內複製'dc'然後捕獲副本也不起作用! – Tom89

+0

湯姆,你沒有看到我*只是*寫過一個示例代碼的相同問題,現在我無法重現它爲我的生活。當我說「正義」時,我的意思是5分鐘前。我改變了一個'std :: cout'的位置,現在不再發生了,並且改變了*返回*沒有關係。不,我沒有這樣做。我會看到我可以再次做到這一點。 (和我運行相同的Xcode /叮噹你,順便說一句)。 – WhozCraig

回答

5

正如注意到@DaveS這可能是一個known gcc bug - 捕獲引用存儲作爲參考。

使用存儲的lambda時,一個好的經驗法則是避免=,因爲應該小心處理存儲狀態。

void DBStorage::dispose(std::shared_ptr<DataChunk>& dc) 
{ 
    std::shared_ptr<DataChunk> data_to_store = dc; 
    backgroundQueue.queueTask([data_to_store]() { // maybe add `,this` to the capture list 
    assert(data_to_store.use_count() == 1); 
    if (data_to_store->isDirty()) { 
     //store to disk 
    } 
    }); 
} 

或:

void DBStorage::dispose(std::shared_ptr<DataChunk> data_to_store) 
{ 
    backgroundQueue.queueTask([data_to_store]() { // maybe add `,this` to the capture list 
    assert(data_to_store.use_count() == 1); 
    if (data_to_store->isDirty()) { 
     //store to disk 
    } 
    }); 
} 

以主動建議的第二位,std::function s爲不lambda表達式,並調用一個theLambda是一種誤導。

+0

謝謝,當我自己寫'auto data_to_store = dc'時,我自己嘗試了這個功能,所以我不再參考!另外,在實際的代碼中''Lambda'實際上被稱爲'pendingJob',但是我將編輯這個OP來使它更容易誤導:) – Tom89

+0

And ...一個已知的GCC bug如此原樣地應用於完全不同的編譯器?這很奇怪,看起來在規範上有分歧? – Tom89

+1

@ Tom89等待,'auto data_to_store = dc;'應該是一個值 - 'auto'就像'T'參數一樣推導出來。/puzzle – Yakk

相關問題