3
votes

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>          ]
3
You've to write [] (std::unique_ptr<int> && p) {} instead of [] (int* p) {}Nawaz
@Nawaz: No, not really. Actually neither the lambda nor a should take the std::unique_ptr through an rvalue-referenceDavid Rodríguez - dribeas
@DavidRodríguez-dribeas: Why?Nawaz
@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<void()> will cause a call with null.David Rodríguez - dribeas
@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 notDavid Rodríguez - dribeas

3 Answers

4
votes

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

2
votes

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>());
0
votes

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.