簡體   English   中英

C ++ lambda不會對按值捕獲的成員調用析構函數

[英]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

IDEOne.com輸出相同的代碼

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM