13

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.

deft_code
  • 57,255
  • 29
  • 141
  • 224
Candy Chiu
  • 6,579
  • 9
  • 48
  • 69
  • This question has already been asked [here](http://stackoverflow.com/q/8640393/20984). However, I don't vote to close since your question contains an alternative (though not-working) solution. – Luc Touraille Apr 23 '12 at 12:57
  • See also [here](http://stackoverflow.com/q/8236521/20984). – Luc Touraille Apr 23 '12 at 13:03

2 Answers2

20

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;
}
Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • Do you really *need* to implement the move-semantic functions, i.e `move_on_copy_wrapper(move_on_copy_wrapper&&)` and `move_on_copy_wrapper& operator=(move_on_copy_wrapper&&)`? The compiler generated ones are not enough? – Nawaz Apr 23 '12 at 13:09
  • 1
    The compiler won't generate them due to the existence of the copy constructor and copy assignment operator. – Anthony Williams Apr 23 '12 at 13:16
  • 1
    "Note that std::bind also requires that its arguments are copyable." That's not true. `std::bind` is usable with move-only types. – Luc Danton Apr 23 '12 at 13:35
  • AFAIK, std::bind should work on movable arguments. However, MSDN's documentation says all arguments should be copy constructible. Anthony, which compiler do you use? http://msdn.microsoft.com/en-us/library/bb982702(v=vs.100).aspx – Candy Chiu Apr 23 '12 at 13:43
  • 1
    OK, I checked: `std::bind` should be usable with move-only arguments, it just creates a move-only result. However, it fails in practice with MSVC 2010 and g++ 4.7. – Anthony Williams Apr 23 '12 at 14:03
  • Sample code didn't compile: error C2558: struct 'move_on_copy_wrapper' : no copy constructor available or copy constructor is declared 'explicit' with [T=std::unique_ptr] – Candy Chiu Apr 23 '12 at 14:39
  • Interesting. It appears MSVC 2010 requires the copy constructor to take `const` arg. Easily fixed: make `value` `mutable`, and add `const` to the copy constructor and copy-assignment operator. – Anthony Williams Apr 23 '12 at 14:49
  • GCC 4.7 can certainly correctly deal with move-only types with `std::bind`. However be careful of the peculiar way `std::bind` forwards the bound arguments to the functor: try for instance `pointer_type p; auto f = std::bind([](pointer_type& p) {}, std::move(p)); f();` with some appropriate `pointer_type`. – Luc Danton Apr 23 '12 at 16:54
  • @AnthonyWilliams, you defined the move ctors by hand. Doesn't `=default` force the compiler to generate the move ctors for you? – deft_code Apr 23 '12 at 17:16
  • Yes, I could have used `=default` for the move constructor and move assignment operator. – Anthony Williams Apr 24 '12 at 12:37
3

Your attempt 2 will almost work.

What's missing is you haven't told your bind call to expect a parameter:

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

The placeholders::_1 is necessary to tell the result of bind that it should expect a parameter passed to it for its operator().

This is also the suggestion given in @marton78's answer here.

Community
  • 1
  • 1
NHDaly
  • 7,390
  • 4
  • 40
  • 45