【发布时间】:2014-03-20 06:42:06
【问题描述】:
今天,我被 XCode 下 lambdas 的这种奇怪行为严重咬伤了——在尝试跟踪 iOS 中围绕代码的几个内存泄漏之后,我将其范围缩小到这个(和类似的)sn-p(s)我使用共享指针将某物的所有权分配给延迟任务:
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; 置空,我希望它调用所有按值捕获的对象的析构函数,从而调用 DataChunk 的析构函数。
但是,看起来在 XCode/LLVM lc 的析构函数下从未调用过;使用mutable 显式调用其dtor,并使用简单的delete 删除std::function 也不起作用。
这是标准行为吗?
我当然可以手动调用dc.reset(),它可以按预期工作,但这很说明使用共享指针没有实际意义。
解决方案 显然是known gcc bug。
贡献
带有 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 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 ()
【问题讨论】:
-
您使用的是什么版本的编译器? GCC 中有一个错误,即引用的按值捕获是引用。 stackoverflow.com/questions/6529177/…
-
clang --version说,Apple LLVM version 5.0 (clang-500.2.75) (based on LLVM 3.3svn)- 这看起来确实可行,但是首先在本地范围内复制dc然后捕获副本也不起作用! -
Tom,你没有看到我只是在为此编写示例代码时遇到了同样的问题,现在我无法终生重现它.当我说“只是”时,我的意思是 5 分钟前。我更改了
std::cout位置,现在它不再发生,更改它 back 没关系。不,我不是在编造这个。我会看到我可以再做一次。 (顺便说一句,我正在运行与您相同的 Xcode/clang)。 -
我在尝试复制到本地范围时失败了...
auto local = lc不会复制指针! @Yakk 建议的代码有效:) -
@Tom89 也许是这样,但您可能会发现我为您的问题提供的输出很有趣。我当然做到了。
标签: c++ xcode c++11 lambda llvm