How to capture a unique_ptr into a lambda expression?
Solution 1
This issue is addressed by lambda generalized capture in 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);});
Solution 2
You cannot permanently capture a unique_ptr
in a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.
This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptr
value into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::move
exists, and this is a good thing.
To do what you want will require either using std::bind
(which would be semi-convoluted, requiring a short sequence of binds
) or just returning a regular old object.
Also, never take unique_ptr
by &&
, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move
. Indeed, it's generally a good idea to never take anything by &&
, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).
Solution 3
The "semi-convoluted" solution using std::bind
as mentioned by Nicol Bolas is not so bad after all:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
Solution 4
A sub-optimal solution that worked for me was to convert the unique_ptr
to a shared_ptr
and then capture the shared_ptr
in the lambda.
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();
};
}
Solution 5
I used this really dodgy workaround, which involves sticking the unique_ptr
inside a shared_ptr
. This is because my code required a unique_ptr
(due to an API restriction) so I couldn't actually convert it to a shared_ptr
(otherwise I'd never be able to get my unique_ptr
back).
My justification for using this abomination is that it was for my test code, and I had to std::bind
a unique_ptr
into the test function call.
// 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);
});
Now calling fnTest()
will call run_test()
while passing the unique_ptr
to it. Calling fnTest()
a second time will result in an assertion failure, because the unique_ptr
has already been moved/lost during the first call.
Earth Engine
When working I focused on writing efficient code with the highest level of abstraction, so the result code can be used in many different situations with minimal modification. At work I use C#/C++/Java, but I am currently writing a Rust interpreter for continuation calculus.
Updated on July 10, 2022Comments
-
Earth Engine almost 2 years
I have tried the following:
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 }; }
But it does not compile. Any ideas?
UPDATE:
AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:
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 }; }
Would it be a good candidate?
UPDATE 1:
I will show my implementation of
move
andcopy
as following: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. }