3

I'd like to capture a variable of type std::vector<std::unique_ptr<MyClass>> in a lambda expression (in other words, "capture by move"). I found a solution which uses std::bind to capture unique_ptr (https://stackoverflow.com/a/12744730/2478832) and decided to use it as a starting point. However, the most simplified version of the proposed code I could get doesn't compile (lots of template mistakes, it seems to try to call unique_ptr's copy constructor).

#include <functional>
#include <memory>

std::function<void ()> a(std::unique_ptr<int>&& param)
{
    return std::bind( [] (int* p) {},
        std::move(param));
}

int main()
{
    a(std::unique_ptr<int>(new int()));
}

Can anybody point out what is wrong with this code?

EDIT: tried changing the lambda to take a reference to unique_ptr, it still doesn't compile.

#include <functional>
#include <memory>

std::function<void ()> a(std::unique_ptr<int>&& param)
{
    return std::bind( [] (std::unique_ptr<int>& p) {}, // also as a const reference
        std::move(param));
}

int main()
{
    a(std::unique_ptr<int>(new int()));
}

Here's Visual Studio 2012 output:

1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(151): error C2248: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<int,std::default_delete<_Ty>>'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1447) : see declaration of 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<const _Ty&>(_Other)' being compiled
1>          with
1>          [
1>              _This=std::unique_ptr<int,std::default_delete<int>>
1>  ,            _Ty=std::unique_ptr<int,std::default_delete<int>>
1>  ,            _Other=const std::unique_ptr<int,std::default_delete<int>> &
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<const _Ty&>(_Other)' being compiled
1>          with
1>          [
1>              _This=std::unique_ptr<int,std::default_delete<int>>
1>  ,            _Ty=std::unique_ptr<int,std::default_delete<int>>
1>  ,            _Other=const std::unique_ptr<int,std::default_delete<int>> &
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : while compiling class template member function 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> &)'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\functional(1152) : see reference to function template instantiation 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> &)' being compiled
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\functional(1152) : see reference to class template instantiation 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' being compiled
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          main.cpp(15) : see reference to class template instantiation 'std::_Bind<false,void,a::<lambda_2ad08ede4c4ce9c02d5497417b633d1d>,std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' being compiled
1>          with
1>          [
1>              _Ty=int
1>          ]
Community
  • 1
  • 1
user2478832
  • 561
  • 5
  • 11
  • You've to write `[] (std::unique_ptr && p) {}` instead of `[] (int* p) {}` – Nawaz Jun 24 '13 at 15:00
  • @Nawaz: No, not really. Actually neither the lambda nor `a` should take the `std::unique_ptr` through an rvalue-reference – David Rodríguez - dribeas Jun 24 '13 at 15:02
  • @DavidRodríguez-dribeas: Why? – Nawaz Jun 24 '13 at 15:07
  • 1
    @Nawaz: You should only use `&&` when you need to differentiate between lvalue and rvalue. This is not the case here, and there is no reason not to take the argument to `a` by reference: by-value suffices. The case of the lambda is different, in this case the signature is saying that the argument to the lambda is a temporary from which you can freely move and destroy the original, but the fact is that it is *not* a temporary, it is a `std::unique_ptr` that lives inside the bound object. If you move out of it, subsequent calls to the `std::function` will cause a call with null. – David Rodríguez - dribeas Jun 24 '13 at 15:22
  • @DavidRodríguez-dribeas: thanks for the subtleties. :-) – Nawaz Jun 24 '13 at 15:29
  • 1
    @Nawaz: I imagine we are going to see far more rvalue-references than make sense. But that is what happens when you have a cool new feature. If you find an rvalue-reference outside of a constructor/assignment, think twice whether it is needed or not – David Rodríguez - dribeas Jun 24 '13 at 15:37

3 Answers3

4

The second argument to bind will be passed to the bound object at the time of call. The problem is that the lambda takes a int*, but the argument is a std::unique_ptr<int> and there is no conversion from the latter to the former.

It should compile (untested) if you change the signature of the lambda to take a std::unique_ptr by reference/const-reference

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

The problem with the version that passes a lambda taking unique_ptr by reference to std::bind is your conversion to std::function - std::function requires functions to be CopyConstructible ([func.wrap.func.con] p7). Try it without the std::function (Live at ideone):

auto f = std::bind([](std::unique_ptr<int>&){},
                   std::make_unique<int>());
Casey
  • 41,449
  • 7
  • 95
  • 125
0

My understanding of the internals of std::bind is that it will always make a copy of the 1st argument object that is being bound to the function-object rather than moving it (even if that argument is an rvalue), so you'll always end up with a call to the copy-constructor for whatever object you are attempting to bind to the function object, and not the move-constructor, even with the use of std::move.

Jason
  • 31,834
  • 7
  • 59
  • 78