54

I have tried the following:

std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
    //The caller given ownership of psomething
    return [psomething](){ 
        psomething->do_some_thing();
        //psomething is expected to be released after this point
    };
}

But it does not compile. Any ideas?

UPDATE:

AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:

std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
    //The caller given ownership of psomething
    return [auto psomething=move(psomething)](){ 
        psomething->do_some_thing();
        //psomething is expected to be released after this point
    };
}

Would it be a good candidate?

UPDATE 1:

I will show my implementation of move and copy as following:

template<typename T>
T copy(const T &t) {
    return t;
}

//process lvalue references
template<typename T>
T move(T &t) {
    return std::move(t);
}

class A{/*...*/};

void test(A &&a);

int main(int, char **){
    A a;
    test(copy(a));    //OK, copied
    test(move(a));    //OK, moved
    test(A());        //OK, temporary object
    test(copy(A()));  //OK, copying temporary object
    //You can disable this behavior by letting copy accepts T &  
    //test(move(A())); You should never move a temporary object
    //It is not good to have a rvalue version of move.
    //test(a); forbidden, you have to say weather you want to copy or move
    //from a lvalue reference.
}
deW1
  • 5,562
  • 10
  • 38
  • 54
Earth Engine
  • 10,048
  • 5
  • 48
  • 78

6 Answers6

82

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);});
mattnewport
  • 13,728
  • 2
  • 35
  • 39
  • 1
    When does the unique pointer get released under these circumstances? When the lambda does? – Leo Aug 18 '14 at 14:43
  • @Leo The unique pointer must be moved to the data block of the lambda (a lambda is just an object with `()` operator) and then released after the lambda object being released. The lambda object as an rvalue can be released after `go.run` if it is not moved. If it is moved it can be released anytime after it, and this makes sense if you want to schedule `do_something_with` to run later on. – Earth Engine Jan 13 '15 at 22:36
  • 4
    Nowadays the example should be `go.run([u = move(u)] ...`. It seems the construct without the `=` was acceptable in 2013 but the Standard got finalized differently. – NonNumeric Jun 06 '16 at 14:42
  • 3
    Is the `u` inside the lambda `const`? – Roi Danton Jul 04 '19 at 15:26
  • 7
    This did not solve my problem since the resulting lambda could not construct a std::function. – sanjivgupta Mar 05 '20 at 16:44
  • This seems like non general solution. What if you inside of the factory, assigning a callback via lambda notation but factory still needs to return or use that unique ptr? – Vlad May 19 '20 at 16:27
  • 3
    @Vlad> you mean, what if you want the factory and the lambda to both have a copy of the unique_ptr? Doesn't this conflict with the very notion of unique_ptr? – spectras Sep 02 '20 at 15:27
45

You cannot permanently capture a unique_ptr in a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.

This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptr value into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::move exists, and this is a good thing.

To do what you want will require either using std::bind (which would be semi-convoluted, requiring a short sequence of binds) or just returning a regular old object.

Also, never take unique_ptr by &&, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move. Indeed, it's generally a good idea to never take anything by &&, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Greate! I would like to mark this as correct answer if you provide some example of the `bind` sequence. – Earth Engine Nov 23 '11 at 04:32
  • 2
    I agree that taking `unique_ptr` by `&&` is not a good idea. However, in general I believe take something by `&&` means "I want the caller actually give me the ownership of something, not just for reference". – Earth Engine Nov 23 '11 at 04:36
  • @EarthEngine: You can get that by taking the argument by *value*. If they pass a temporary, then the temporary will be moved into your argument value. If they pass a non-temporary, they still have to use `std::move`, which will cause the movement to happen into your *argument*. The way you're doing it means that your function does not *have* to take ownership of it. [My post here](http://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function/8114913#8114913) explains this in greater detail. – Nicol Bolas Nov 23 '11 at 04:43
  • For unique_ptr, which is not copyable, your idea is absolutely right. For other objects like shared_ptr, if you take it by value, in general you got a copy of the object unless you say `move`. On the other hand, taking it by `&&` forbidden default copy behavior and forces the user to use explicit `move`. In my practice, I implemented a parallel `copy` template to force the copy behavior. Of cause, there is still no guarantee that the `std::move` really moves anything, but my version of `move` do, because it is returning a value. – Earth Engine Nov 23 '11 at 05:41
  • @EarthEngine: What if the user doesn't *want* to move a copyable object? What if they want to copy it? Why should they have to use a special template to do something simple that the API would be able to do if you just let it? – Nicol Bolas Nov 23 '11 at 06:23
  • If the function accepts value the story is remain the same: you have to move(by `std::move`) or copy(by value), no other choice. The motivation to introduce some special templates is just to make things explicit. – Earth Engine Nov 23 '11 at 11:19
  • 3
    "*Indeed, it's generally a good idea to never take anything by `&&`, unless you're writing the move constructor/assignment operator.*" Or unless you're implementing perfect forwarding... – ildjarn Nov 23 '11 at 19:04
  • @NicolBolas, how do you make a function taking a movable-only object as a parameter strongly exception safe without taking the parameter by `&&`? You don't want the argument to be moved from until the moment the call is certain to succeed. – avakar Oct 30 '12 at 22:41
  • @avakar: I'm not sure how that violates strong exception safety, since the data gets deleted one way or another. Doing it my way, the person who moved it into your function has an empty object, which is something they know for certain to begin with, whether an exception would be thrown or not. I don't see the problem. – Nicol Bolas Oct 30 '12 at 23:46
  • @NicolBolas, that's the point, if the callee throws, you will be left with an empty object. If you pass the argument by reference, the callee can make sure that it will not be moved from in case of a throw. Does that make sense? You want the argument intact in case of an error so that you can recover by alternative means. – avakar Oct 31 '12 at 08:37
  • @avakar: "*that's the point, if the callee throws, you will be left with an empty object.*" And if it returned normally, you'd *still* be left with an empty object. You will *always* have an empty object. Which means your code will have to be designed to accept that fact. Therefore, whatever "alternative means" you use to recover will be designed around *not* having that data anymore. This is not a difficult problem; if you were not prepared to *lose* that data, you shouldn't have transferred ownership of it. – Nicol Bolas Oct 31 '12 at 15:26
  • @NicolBolas, no, if I pass by reference, I will not end up with an empty object. The callee can be written so that the argument is not moved from until the moment the operation is guaranteed to succeed. – avakar Oct 31 '12 at 15:48
  • @avakar: I get that. My point is *why*? Why go through the effort? You are *giving away the memory*. My way means that you don't have to check to see if your use of `std::move` actually *moved* something. You gave away the resource, ergo it's gone, no matter *how* the function exited. Your "alternative means" of recovery would simply take this into account. – Nicol Bolas Oct 31 '12 at 15:49
  • 5
    This answer should be updated with recent changes in the draft for c++14. – Klaim Jun 06 '13 at 17:31
20

The "semi-convoluted" solution using std::bind as mentioned by Nicol Bolas is not so bad after all:

std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
    return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
                     std::move(psomething));
}
marton78
  • 3,899
  • 2
  • 27
  • 38
16

A sub-optimal solution that worked for me was to convert the unique_ptr to a shared_ptr and then capture the shared_ptr in the lambda.

std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
    //The caller given ownership of psomething
    std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
    return [psomethingShared]()
    {
        psomethingShared->do_some_thing();
    };
}
tcb
  • 4,408
  • 5
  • 34
  • 51
4

I used this really dodgy workaround, which involves sticking the unique_ptr inside a shared_ptr. This is because my code required a unique_ptr (due to an API restriction) so I couldn't actually convert it to a shared_ptr (otherwise I'd never be able to get my unique_ptr back).

My justification for using this abomination is that it was for my test code, and I had to std::bind a unique_ptr into the test function call.

// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));

std::function<void()> fnTest = std::bind([this, sh, input, output]() {
    // Move unique_ptr back out of shared_ptr
    auto unique = std::move(*sh.get());

    // Make sure unique_ptr is still valid
    assert(unique);

    // Move unique_ptr over to final function while calling it
    this->run_test(std::move(unique), input, output);
});

Now calling fnTest() will call run_test() while passing the unique_ptr to it. Calling fnTest() a second time will result in an assertion failure, because the unique_ptr has already been moved/lost during the first call.

Malvineous
  • 25,144
  • 16
  • 116
  • 151
1

One also need to know, that lambdas capturing unique_ptr cannot be converted into std::function because std::function requires that the callable object is copyable.

auto lambdaWithoutCapture = [](){return 1;}; //Can be std::function
auto lambdaWithCapture = [=](){return 1;}; //Can be std::function
auto lambdaWithCapture2 = [&](){return 1;}; //Can be std::function
auto lambdaWithCapture3 = [uptrProblematic = std::move(uptrProblematic)]() mutable {return 1;}; //Can't be std::function

Therefore, if you don't have to specify return type of the function, you can use such approach which does not use std::function. But you need to know, that this will only work in local scope. You can't declare auto workerFactory(); in header file, as this will raise compilation error.

auto workerFactory()
{
    std::unique_ptr uptrProblematic = std::make_unique<int>(9);
    int importantData = 71;
    return [=, uptrProblematic = std::move(uptrProblematic)](std::string input) mutable -> int {
        std::cout << "Problematic variable is equal to: " << *uptrProblematic << "\n";
        std::cout << "Important data is equal to: " << importantData << "\n";
        std::cout << "Input equal to: " << input << "\n";
        return 9;
    };
}

int main()
{
    auto worker = workerFactory();
    worker("Test");
}
Adam Kuzański
  • 509
  • 4
  • 12
  • 1
    _"std::function can only by a function"_ -- [this is absolutely not true](https://compiler-explorer.com/z/ja7b5GsMv); it needs to be a callable object, which can definitely contain state. Are you perhaps thinking of the fact that `std::function` [requires that the callable object be **copyable**](https://compiler-explorer.com/z/da19hd5sj)? – Human-Compiler Dec 29 '22 at 13:35
  • Yes, my bad, you are right. I was thinking about copyable – Adam Kuzański Dec 29 '22 at 13:42
  • @Human-Compiler do you have any additional remarks for edited question? If there is anything to improve, I will be glad to do it – Adam Kuzański Jan 03 '23 at 13:07