[英]C++ lambda won't call the destructor on members captured by value
I've been badly bitten by this strange behavior of lambdas under XCode today - after trying to trace several memory leaks in iOS around the code, I've narrowed it down to this (and similar) snippet(s) where I assign the ownership of something to a deferred task using a shared pointer: 我今天在XCode下对lambdas的这种奇怪行为感到非常痛苦 - 在尝试跟踪iOS中围绕代码的几个内存泄漏之后,我将其缩小到这个(和类似的)代码片段,在那里我分配所有权使用共享指针的延迟任务的东西:
void DBStorage::dispose(std::shared_ptr<DataChunk>& dc)
{
backgroundQueue.queueTask([=]() {
assert( dc.use_count() == 1 );
if (dc->isDirty()) {
//store to disk
}
});
}
(Note that the shared pointer's use count is always 1 when the lambda is run) (注意,运行lambda时共享指针的使用计数始终为1)
After execution, this task is null-ified with pendingJob = nullptr;
执行后,此任务为pendingJob = nullptr;
- pendingJob = nullptr;
which I expected to call the destructor of all the captured-by-value objects, and consequently DataChunk
's destructor. 我希望调用所有按值捕获的对象的析构函数,以及DataChunk
的析构函数。 However, it looks like that under XCode/LLVM lc
's destructor is never called; 但是,看起来在XCode / LLVM下, lc
的析构函数永远不会被调用; calling its dtor explicitly, using mutable
, and deleting the std::function
with a simple delete
didn't work either. 使用mutable
显式调用它的dtor,并用简单的delete
std::function
也不起作用。
Is this standard behavior? 这是标准行为吗? I can of course manually call dc.reset()
and it works as expected, but this quite makes the point of using a shared pointer moot. 我当然可以手动调用dc.reset()
并按预期工作,但这很有可能使用共享指针。
Solution Apparently, it is a known gcc bug . 解决方案显然,这是一个已知的gcc bug 。
Contrib 的Contrib
Stand-alone sample with output from Xcode 5.0.2/clang 3.3 具有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 Output 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 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 Output 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 ()
As noted by @DaveS this could be a known gcc bug -- captured references are stored as references. 正如@DaveS所指出的,这可能是一个已知的gcc错误 - 捕获的引用存储为引用。
A good rule of thumb when working with stored lambdas is to avoid =
, as stored state should be treated with care. 使用存储的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
}
});
}
or: 要么:
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
}
});
}
as a second bit of unsolicited advice, std::function
s are not lambdas, and calling one theLambda
is misleading. 作为第二位未经请求的建议, std::function
s不是lambdas,调用一个theLambda
会产生误导。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.