C++ lambda不会对按值捕获的成员调用析构函数

Tom*_*m89 9 c++ lambda xcode llvm c++11

我今天在XCode下对lambdas的这种奇怪行为感到非常痛苦 - 在尝试跟踪iOS中围绕代码的几个内存泄漏之后,我将其缩小到这个(和类似的)代码片段,在那里我分配所有权使用共享指针的延迟任务的东西:

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

        if (dc->isDirty()) {
            //store to disk
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

(注意,运行lambda时共享指针的使用计数始终为1)

在执行之后,这个任务是null-ified pendingJob = nullptr;,我希望它可以调用所有按值捕获的对象的析构函数,从而调用析DataChunk构函数.但是,看起来在XCode/LLVM下 lc的析构函数永远不会被调用; 明确地调用它的dtor,使用mutable和删除std::function简单的delete也不起作用.

这是标准行为吗?我当然可以手动调用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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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 ()
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 5

正如@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
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

要么:

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
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

作为第二点未经请求的建议,std::functions不是lambdas,并且调用一个theLambda是误导性的.

  • @Casey` [&]`仅在创建在当前范围结束之前死亡(具有所有副本)的lambda时.`[vars,explicit]`创建持久lambda时.`[=]`在编写丢弃代码时或在重构过程中. (2认同)