[英]C++ lambda won't call the destructor on members captured by value
我今天在XCode下對lambdas的這種奇怪行為感到非常痛苦 - 在嘗試跟蹤iOS中圍繞代碼的幾個內存泄漏之后,我將其縮小到這個(和類似的)代碼片段,在那里我分配所有權使用共享指針的延遲任務的東西:
void DBStorage::dispose(std::shared_ptr<DataChunk>& dc)
{
backgroundQueue.queueTask([=]() {
assert( dc.use_count() == 1 );
if (dc->isDirty()) {
//store to disk
}
});
}
(注意,運行lambda時共享指針的使用計數始終為1)
執行后,此任務為pendingJob = nullptr;
- pendingJob = nullptr;
我希望調用所有按值捕獲的對象的析構函數,以及DataChunk
的析構函數。 但是,看起來在XCode / LLVM下, lc
的析構函數永遠不會被調用; 使用mutable
顯式調用它的dtor,並用簡單的delete
std::function
也不起作用。
這是標准行為嗎? 我當然可以手動調用dc.reset()
並按預期工作,但這很有可能使用共享指針。
解決方案顯然,這是一個已知的gcc bug 。
的Contrib
具有Xcode 5.0.2 / clang 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
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 ()
正如@DaveS所指出的,這可能是一個已知的gcc錯誤 - 捕獲的引用存儲為引用。
使用存儲的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不是lambdas,調用一個theLambda
會產生誤導。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.