How to Capture a Unique_Ptr into a Lambda Expression

How to capture a unique_ptr into a lambda expression?

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);});

Capture and move a unique_ptr in a c++14 lambda expression

The operator () of a lambda is const by default, and you can't move from a const object.

Declare it mutable if you want to modify the captured variables.

auto lambda = [ capturedStr = std::move(str) ] () mutable {
// ^^^^^^^^^^
cout << *capturedStr.get() << endl;
auto str2 = std::move(capturedStr);
};

How to capture std::unique_ptr by move for a lambda in std::for_each

Update: you can capture a movable variable in a lambda from C++14 onwards.

std::for_each(arr.begin(), arr.end(), [p=std::move(p)](int& i) { i+=*p; });

You cannot capture a movable variable into a lambda in any straightforward way in C++11.

Lambdas capture by copy or by reference. Thus, to capture a move-only variable, you have to wrap it in an object where copying => moving (such as std::auto_ptr). This is a nasty hack.

In your example, you can just capture by reference, but if this was just simplified code it may not do what you wanted with the real code:

std::for_each(arr.begin(), arr.end(), [&p](int& i) { i+=*p; });

Here's a copy-move-only wrapper:

template<typename T>
struct move_on_copy_wrapper
{
mutable T value;

move_on_copy_wrapper(T&& t):
value(std::move(t))
{}

move_on_copy_wrapper(move_on_copy_wrapper const& other):
value(std::move(other.value))
{}

move_on_copy_wrapper(move_on_copy_wrapper&& other):
value(std::move(other.value))
{}

move_on_copy_wrapper& operator=(move_on_copy_wrapper const& other)
{
value=std::move(other.value);
return *this;
}

move_on_copy_wrapper& operator=(move_on_copy_wrapper&& other)
{
value=std::move(other.value);
return *this;
}

};

You can then use it like this:

int main()
{
std::unique_ptr<int> p(new int(3));
move_on_copy_wrapper<std::unique_ptr<int>> mp(std::move(p));

[mp]()
{
std::cout<<"*mp.value="<<*mp.value<<std::endl;
}
();

std::cout<<"p="<<p.get()<<", mp="<<mp.value.get()<<std::endl;
}

When moving a unique_ptr into a lambda, why is it not possible to call reset?

  1. Why does this happen?

Because the function-call operator of a lambda,

Unless the keyword mutable was used in the lambda-expression, the function-call operator is const-qualified and the objects that were captured by copy are non-modifiable from inside this operator().

and


  1. Is it possible to capture the std::unique_ptr in another way which allows to call reset() within the lambda

You need to mark it mutable.

mutable: allows body to modify the parameters captured by copy, and to call their non-const member functions

e.g.

auto l = [v = std::move(u)]() mutable {
v.reset();
};

How to return a lambda with a captured unique_ptr

As pointed out in comments - callable wrapped in std::function has to copy constructible. So instead of returning function just return lambda itself like that:

auto get_func()
{
auto i = std::make_unique<int>(2);
return [i=std::move(i)]() {
return *i;
};
}

pass a lambda that captures a unique_pointer

std::function must be both CopyConstructible and CopyAssignable, which means that it must be able to copy its target. Unfortunately, since your lambda is not copyable, you cannot store it in a std::function.

You will have to resort to another implementation of callable type-erasure that works with movable-only objects.

Moving unique_ptr inside a lambda function gives me a compiler error on C++17

The issue is that lambda's operator () is declared const and with the move you try to modify the unique pointer j.

Declare it mutable via auto t = std::thread([j = std::move(job)]() mutable ...) or pass the unique_ptr as an argument to the lambda.

How to capture a unique_ptr in a std::function

std::function objects can all be copied

std::function is a type-erasure object that supports copying the object stored.

When you store a std::unique_ptr in a lambda, that lambda does not support being copied.

So std::function quite rightly complains. It is a type that can be copied, and when passed in something is works out how to copy it. "I cannot copy it" isn't a valid answer; all std::functions can be copied.

Industrial strength solution:

There are two common approaches to solve this problem. First, you store the std::function's state in a std::shared_ptr of some kind. Second, you write or find a non-copying std::function and use that instead.

More "modern" std::function replacement libraries support a number of useful things:

  1. function views, that do not own what they wrap.
  2. move-only function objects, that don't support copying.
  3. multiple-overload function objects, that support more than 1 signature at once.
  4. fixed-sized buffers, that fail to compile if there isn't enough automatic storage instead of heap allocating.
  5. trivially copyable function objects

I've personally had a need for every one of the above for various special purposes.

Then you'd use moveonly_function<void()> when you don't need to copy the callable, and your code compiles.

However, this is probably too heavy for your needs right now.

A quick solution is:

template<class F>
auto make_shared_function( F&& f ) {
return
[pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))]
(auto&&...args)->decltype(auto)
{
return (*pf)( decltype(args)(args)... );
};
}

now whenever you run into this problem:

// This does not compile.
std::function<void()> func = make_shared_function([p = move(ptr)] { });

and the state of the callable object is now stored in a shared ptr.



Related Topics



Leave a reply



Submit