如何在Lambda中正确移动unique_ptr并存储到deque?
问题:将unique_ptr移动到Lambda后存入deque编译失败
尝试将unique_ptr资源移动到Lambda表达式中,并将其存储到deque容器中,示例代码如下:
#include <iostream> #include <cstring> #include <memory> #include <thread> #include <deque> // base class class MyBase { public: char* _data; int _len; public: MyBase(int l) : _len(l) { _data = new char(_len); // 注意:此处应为new char[_len],否则只分配单个char } virtual ~MyBase() { delete[] _data; } MyBase(const MyBase& src) : _len(src._len) { _data = new char[_len]; std::memcpy(_data, src._data, _len); } MyBase(MyBase&& src) : _len(std::move(src._len)) { _data = src._data; src._data = nullptr; } MyBase& operator = (const MyBase& src) { if (&src == this) return *this; _len = src._len; if (_data != nullptr) delete[] _data; _data = new char[_len]; std::memcpy(_data, src._data, _len); } MyBase& operator = (MyBase&& src) { if (&src == this) return *this; _len = std::move(src._len); _data = src._data; src._data = nullptr; } }; class MyTest : public MyBase { int _d; public: MyTest(int d, int l) : MyBase(l) , _d(d) { } ~MyTest() { } MyTest(const MyTest& src) : MyBase(src) , _d(src._d) { } MyTest(MyTest&& src) : MyBase(std::move(src)) , _d(std::move(src._d)) { } MyTest& operator = (const MyTest& src) { if (&src == this) return *this; MyBase::operator = (src); _d = src._d; return *this; } MyTest& operator = (MyTest&& src) { if (&src == this) return *this; MyBase::operator = (std::move(src)); _d = std::move(src._d); return *this; } }; int main() { std::unique_ptr<MyTest> m = std::make_unique<MyTest>(10, 5); // move resource std::thread t = std::thread([s = std::move(m)]() { std::cout << "start" << std::endl; std::deque<std::unique_ptr<MyTest>> container; container.push_back(std::move(s)); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "finish" << std::endl; }); t.join(); return 0; }
编译错误信息
In file included from /usr/include/x86_64-linux-gnu/c++/9/bits/c++allocator.h:33, from /usr/include/c++/9/bits/allocator.h:46, from /usr/include/c++/9/string:41, from /usr/include/c++/9/bits/locale_classes.h:40, from /usr/include/c++/9/bits/ios_base.h:41, from /usr/include/c++/9/ios:42, from /usr/include/c++/9/ostream:38, from /usr/include/c++/9/iostream:39, from main.cpp:9: /usr/include/c++/9/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::unique_ptr<MyTest>; _Args = {const std::unique_ptr<MyTest, std::default_delete<MyTest> >&}; _Tp = std::unique_ptr<MyTest>]’: /usr/include/c++/9/bits/alloc_traits.h:483:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::unique_ptr<MyTest>; _Args = {const std::unique_ptr<MyTest, std::default_delete<MyTest> >&}; _Tp = std::unique_ptr<MyTest>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::unique_ptr<MyTest> >]’ /usr/include/c++/9/bits/stl_deque.h:1574:30: required from ‘void std::deque<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<MyTest>; _Alloc = std::allocator<std::unique_ptr<MyTest> >; std::deque<_Tp, _Alloc>::value_type = std::unique_ptr<MyTest>]’ <span class="error_line" onclick="ide.gotoLine('main.cpp',130)"></span>main.cpp:130:41: required from here /usr/include/c++/9/ext/new_allocator.h:146:4: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = MyTest; _Dp = std::default_delete]’ 146 | { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/c++/9/memory:80, from main.cpp:11: /usr/include/c++/9/bits/unique_ptr.h:414:7: note: declared here 414 | unique_ptr(const unique_ptr&) = delete; | ^~~~~~~~~~
问题原因与修正方案
原因分析
默认情况下,Lambda表达式的operator()是const成员函数,这意味着Lambda内部捕获的变量s是const的。当执行std::move(s)时,得到的是const std::unique_ptr<MyTest>&&类型,而std::unique_ptr的拷贝构造函数已被显式删除,只能接受非const的右值进行移动构造,因此编译器报错。
修正方法
给Lambda添加mutable关键字,取消其operator()的const属性,这样捕获的s在Lambda内部可以被修改(移动操作会修改原对象的状态):
std::thread t = std::thread([s = std::move(m)]() mutable // 添加mutable { std::cout << "start" << std::endl; std::deque<std::unique_ptr<MyTest>> container; container.push_back(std::move(s)); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "finish" << std::endl; });
额外提示
代码中MyBase构造函数里的_data = new char(_len);存在问题:此处使用圆括号()只会分配单个char并初始化为_len的值,而非长度为_len的char数组,应改为_data = new char[_len];,避免后续内存访问越界或析构时的未定义行为。
内容的提问来源于stack exchange,提问作者Pasha
相关产品推荐
相关产品推荐

