Capture and move a unique_ptr in a c++14 lambda expression

15,163

Solution 1

The operator () of a lambda is const by default, and you can't move from a const object.

Declare it mutable if you want to modify the captured variables.

auto lambda = [ capturedStr = std::move(str) ] () mutable {
//                                             ^^^^^^^^^^
    cout << *capturedStr.get() << endl;
    auto str2 = std::move(capturedStr);
};

Solution 2

auto lambda = [ capturedStr = std::move(str) ] {
   cout << *capturedStr.get() << endl;
   auto str2 = std::move(capturedStr); // <--- Not working, why?
};

To give more detail, the compiler is effectively making this transformation:

class NameUpToCompiler
{
    unique_ptr<string> capturedStr;  // initialized from move assignment in lambda capture expression

    void operator()() const
    {
        cout << *capturedStr.get() << endl;
        auto str2 = std::move(capturedStr);  // move will alter member 'captureStr' but can't because of const member function.
    }
}

The use of mutable on the lambda will remove the const from the operator() member function, therefore allowing the members to be altered.

Solution 3

To make the advice more explicit: add mutable: http://coliru.stacked-crooked.com/a/a19897451b82cbbb

#include <memory>

int main()
{
    std::unique_ptr<int> pi(new int(42));

    auto ll = [ capturedInt = std::move(pi) ] () mutable { };
}
Share:
15,163

Related videos on Youtube

MartinMoizard
Author by

MartinMoizard

SOreadytohelp

Updated on March 22, 2022

Comments

  • MartinMoizard
    MartinMoizard about 2 years

    I am capturing a unique_ptr in a lambda expression this way:

    auto str = make_unique<string>("my string");
    auto lambda = [ capturedStr = std::move(str) ] {
       cout << *capturedStr.get() << endl;
    };
    lambda();
    

    It works great until I try to move capturedStr to another unique_ptr. For instance, the following is not working:

    auto str = make_unique<string>("my string");
    auto lambda = [ capturedStr = std::move(str) ] {
        cout << *capturedStr.get() << endl;
        auto str2 = std::move(capturedStr); // <--- Not working, why?
    };
    lambda();
    

    Here is the output from the compiler:

    .../test/main.cpp:11:14: error: call to implicitly-deleted copy
    constructor of 'std::__1::unique_ptr<std::__1::basic_string<char>,
    std::__1::default_delete<std::__1::basic_string<char> > >'
            auto str2 = std::move(capturedStr);
                 ^      ~~~~~~~~~~~~~~~~~~~~~~ ../include/c++/v1/memory:2510:31: note: copy constructor is implicitly
    deleted because 'unique_ptr<std::__1::basic_string<char>,
    std::__1::default_delete<std::__1::basic_string<char> > >' has a
    user-declared move constructor
        _LIBCPP_INLINE_VISIBILITY unique_ptr(unique_ptr&& __u) _NOEXCEPT
                                  ^ 1 error generated.
    

    Why isn't it possible to move capturedStr?

    • T.C.
      T.C. over 9 years
      Lambdas' operator () is const unless it's declared mutable, and you can't move from a const object.
    • MartinMoizard
      MartinMoizard over 9 years
      Thanks @T.C., can you write an answer, I'll accept it
  • sehe
    sehe over 9 years
    I thought you weren't gonna post :) +1