Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

lambda

I am learning the new features in c++11 and came across this problem. I'd like to capture an unique_ptr by moving it inside a lambda as an argument for for_each.

set up:

std::array<int,4> arr = {1,3,5,6};
std::unique_ptr<int> p(new int);  (*p) = 3;

attempt 1 - doesn't work because unique_ptr doesn't have a copy constructor. c++0x doesn't specify the pass by move syntax.

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

attempt 2 - use bind to bind a moved copy of p to a function that takes int&:

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

Compiler complains that 'result' : symbol is neither a class template nor a function template.

The main purpose of the exercise is to understand how a movable variable be captured in a lambda that's cached for later use.

like image 482
Candy Chiu Avatar asked Apr 23 '12 12:04

Candy Chiu


People also ask

Can Unique_ptr be moved?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.

Does Lambda capture reference by value?

The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y , which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes.

Can std :: function store Lambda?

Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.


1 Answers

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;
}
like image 93
Anthony Williams Avatar answered Oct 13 '22 23:10

Anthony Williams