Lambda中捕获std::move的std::unique_ptr编译失败原因咨询
第一个示例:函数按值接收std::unique_ptr
代码
#include <memory> void sink(std::unique_ptr<int> p) { // Do something with `p` } int main() { auto p = std::make_unique<int>(42); auto f = [p = std::move(p)](){ sink(std::move(p)); }; f(); return 0; }
错误信息
<source>:10:13: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' 10 | sink(std::move(p)); | ~~~~^~~~~~~~~~~~~~ In file included from /opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/memory:78, from <source>:1: /opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/unique_ptr.h:516:7: note: declared here 516 | unique_ptr(const unique_ptr&) = delete; | ^~~~~~~~~~ <source>:10:13: note: use '-fdiagnostics-all-candidates' to display considered candidates 10 | sink(std::move(p)); | ~~~~^~~~~~~~~~~~~~ <source>:3:32: note: initializing argument 1 of 'void sink(std::unique_ptr<int>)' 3 | void sink(std::unique_ptr<int> p) { | ~~~~~~~~~~~~~~~~~~~~~^ Compiler returned: 1
原因解析
Lambda表达式的operator()默认是const成员函数,这意味着Lambda内部捕获的所有变量都被视为const类型。这里捕获的p在Lambda内部是const std::unique_ptr<int>,调用std::move(p)会生成一个const std::unique_ptr<int>&&类型的右值。
当尝试将这个const右值传递给sink(需要std::unique_ptr<int>)时,编译器会尝试调用std::unique_ptr的拷贝构造函数——因为const右值无法触发移动构造(移动构造需要非const的右值),而std::unique_ptr的拷贝构造函数是被显式删除的,因此编译报错。
第二个示例:函数按右值引用接收std::unique_ptr
代码
#include <memory> void sink(std::unique_ptr<int> &&p) { // Do something with `p` } int main() { auto p = std::make_unique<int>(42); auto f = [p = std::move(p)](){ sink(std::move(p)); }; f(); return 0; }
错误信息
<source>: In lambda function: <source>:10:23: error: binding reference of type 'std::unique_ptr<int>&&' to 'std::remove_reference<const std::unique_ptr<int>&>::type' {aka 'const std::unique_ptr<int>'} discards qualifiers 10 | sink(std::move(p)); | ~~~~~~~~~^~~ <source>:3:34: note: initializing argument 1 of 'void sink(std::unique_ptr<int>&&)' 3 | void sink(std::unique_ptr<int> &&p) { | ~~~~~~~~~~~~~~~~~~~~~~~^ Compiler returned: 1
原因解析
同样是因为Lambda的operator()默认是const成员函数,捕获的p在Lambda内部是const std::unique_ptr<int>。std::move(p)生成的是const std::unique_ptr<int>&&,而sink的参数是std::unique_ptr<int>&&(非const右值引用)。
C++中,非const的右值引用无法绑定到const右值——因为这会允许修改原本被const保护的对象,违反const语义。因此编译器报错,提示丢弃了const限定符。
解决方法
要解决这个问题,只需要在Lambda声明时添加mutable关键字,这样Lambda的operator()就不再是const成员函数,内部捕获的变量也会变成非const类型,std::move就能生成正确的非const右值:
#include <memory> void sink(std::unique_ptr<int> p) { // Do something with `p` } int main() { auto p = std::make_unique<int>(42); // 添加mutable关键字 auto f = [p = std::move(p)]() mutable { sink(std::move(p)); }; f(); return 0; }
第二个示例同样只需添加mutable即可正常编译。
内容的提问来源于stack exchange,提问作者Semyon Burov

