Captura y mueve un ptr único en una expresión lambda de c++14


Estoy capturando un unique_ptr en una expresión lambda de esta manera:

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

Funciona muy bien hasta que intento mover capturedStr a otro unique_ptr. Por ejemplo, lo siguiente no funciona:

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

Aquí está la salida del compilador:

.../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.

¿por Qué no es posible mover capturedStr?

Author: senshin, 2015-01-06

3 answers

El operator () de una lambda es const por defecto, y no se puede mover desde un objeto const.

Declararlo mutable si desea modificar las variables capturadas.

auto lambda = [ capturedStr = std::move(str) ] () mutable {
//                                             ^^^^^^^^^^
    cout << *capturedStr.get() << endl;
    auto str2 = std::move(capturedStr);
};
 45
Author: T.C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-06 14:20:25
auto lambda = [ capturedStr = std::move(str) ] {
   cout << *capturedStr.get() << endl;
   auto str2 = std::move(capturedStr); // <--- Not working, why?
};

Para dar más detalles el compilador está haciendo efectivamente esta transformación:

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.
    }
}

El uso de mutable en la lambda eliminará la const de la función miembro operator (), por lo tanto, permitiendo que los miembros sean alterados.

 6
Author: rparolin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-07 07:29:01

Para hacer el consejo más explícito: añadir 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 { };
}
 5
Author: sehe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-06 14:20:39