如何将 unique_ptr 捕获到 lambda 表达式中?
Posted
技术标签:
【中文标题】如何将 unique_ptr 捕获到 lambda 表达式中?【英文标题】:How to capture a unique_ptr into a lambda expression? 【发布时间】:2012-01-04 09:51:05 【问题描述】:我尝试了以下方法:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething)
//The caller given ownership of psomething
return [psomething]()
psomething->do_some_thing();
//psomething is expected to be released after this point
;
但它不能编译。有什么想法吗?
更新:
正如建议的那样,需要一些新的语法来明确指定我们需要将所有权转移给 lambda,我现在正在考虑以下语法:
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething)
//The caller given ownership of psomething
return [auto psomething=move(psomething)]()
psomething->do_some_thing();
//psomething is expected to be released after this point
;
它会是一个好的候选人吗?
更新 1:
我将展示我对move
和copy
的实现,如下所示:
template<typename T>
T copy(const T &t)
return t;
//process lvalue references
template<typename T>
T move(T &t)
return std::move(t);
class A/*...*/;
void test(A &&a);
int main(int, char **)
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
【问题讨论】:
【参考方案1】:lambda generalized capture 在 C++14 中解决了这个问题:
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]do_something_with(u););
【讨论】:
这种情况下唯一指针什么时候释放? lambda 什么时候做? 现在这个例子应该是go.run([u = move(u)] ...
。似乎没有 =
的构造在 2013 年是可以接受的,但标准的最终确定方式不同。
u
在 lambda const
内吗?
这并没有解决我的问题,因为生成的 lambda 无法构造 std::function。
@Vlad> 你的意思是,如果你想让工厂和 lambda 都拥有 unique_ptr 的副本怎么办?这与 unique_ptr 的概念不冲突吗?【参考方案2】:
您不能在 lambda 中永久捕获 unique_ptr
。事实上,如果你想在 lambda 中永久捕获任何东西,它必须是可复制;仅仅可移动是不够的。
这可能被认为是 C++11 中的一个缺陷,但您需要一些语法来明确表示您想将 unique_ptr
值移动到 lambda 中。 C++11 规范的措辞非常谨慎,以防止对命名变量进行隐式移动;这就是std::move
存在的原因,这是一件好事。
要执行您想要的操作,需要使用std::bind
(这将是半卷积,需要binds
的短序列)或只返回一个常规的旧对象。
另外,永远不要将unique_ptr
与&&
联系起来,除非您实际上正在编写它的移动构造函数。只看价值;用户可以按价值提供它的唯一方法是使用std::move
。实际上,除非您正在编写移动构造函数/赋值运算符(或实现转发功能),否则永远不要通过 &&
获取任何东西通常是个好主意。
【讨论】:
太好了!如果您提供bind
序列的一些示例,我想将此标记为正确答案。
我同意将unique_ptr
带到&&
不是一个好主意。但是,总的来说,我认为 take something by &&
的意思是“我希望调用者实际上给我某物的所有权,而不仅仅是为了参考”。
@EarthEngine:您可以通过 value 获取参数。如果他们传递了一个临时值,那么这个临时值将被移动到您的参数值中。如果他们通过了一个非临时的,他们仍然必须使用std::move
,这将导致移动发生在你的argument中。你这样做的方式意味着你的函数没有必须拥有它。 My post here 更详细地解释了这一点。
"确实,除非您正在编写移动构造函数/赋值运算符,否则永远不要通过 &&
获取任何内容通常是个好主意。" 或者除非您是实现完美转发...
此答案应根据 c++14 草案的最新更改进行更新。【参考方案3】:
Nicol Bolas 提到的使用std::bind
的“半卷积”解决方案毕竟还不错:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
return std::bind([] (std::unique_ptr<MyClass>& p) p->do_some_thing(); ,
std::move(psomething));
【讨论】:
在 c++11 中无法编译 由于 std::function 需要是可复制的,我看不出这是如何工作的。【参考方案4】:对我有用的次优解决方案是将unique_ptr
转换为shared_ptr
,然后在lambda 中捕获shared_ptr
。
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
psomethingShared->do_some_thing();
;
【讨论】:
喜欢这个解决方案(用于 C++11)。就像将 unique 更改为 shared 一样简单。【参考方案5】:我使用了这个非常狡猾的解决方法,其中包括将unique_ptr
粘贴在shared_ptr
中。这是因为我的代码需要 unique_ptr
(由于 API 限制),所以我实际上无法将其转换为 shared_ptr
(否则我永远无法找回我的 unique_ptr
)。
我使用这个可憎的东西的理由是它是为了我的测试代码,我必须在测试函数调用中std::bind
unique_ptr
。
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]()
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
);
现在调用fnTest()
将调用run_test()
,同时将unique_ptr
传递给它。第二次调用fnTest()
将导致断言失败,因为unique_ptr
在第一次调用期间已经被移动/丢失。
【讨论】:
以上是关于如何将 unique_ptr 捕获到 lambda 表达式中?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 lambda init-capture 对 unique_ptr 不起作用?
在 c++14 lambda 表达式中捕获和移动 unique_ptr
为啥我不能在 C++14 的 lambda 中移动 std::unique_ptr?