Подтвердить что ты не робот

Перемещение с лямбдами

При использовании лямбда-функций скажем, что вы решили скопировать переменную (с обозначением [=]). Если вы еще никогда не ссылаетесь на эту переменную, компилятор разрешил переместить ее в результирующий объект функции?

Изменить: например, я написал фрагмент для переадресации вызовов по потокам. Вот пример, который делает это.

extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) {
     std::string file(filename);
     p->make_call([=]() {
         p->file_updated(std::move(file), offset, added);
     });
}

Но, очевидно, переменная файла не должна проходить мимо лямбда-определения, и, действительно, лямбда вызывается только один раз, поэтому я переместил копию.

4b9b3361

Ответ 1

Если вы еще никогда не ссылаетесь на эту переменную, компилятор разрешил переместить ее в результирующий объект функции?

Нет. Единственная ситуация, когда компилятору разрешено заменять копию движением, - это те же ситуации, когда разрешено выполнять копирование. Эти ситуации включают возвращение локального объекта по значению или инициализацию объекта с временным. В этих случаях компилятору разрешается копировать копию, создавая источник и нацеливая один и тот же объект. Если компилятор не может это сделать по какой-либо причине, он должен рассматривать исходный объект как rvalue относительно разрешения перегрузки для выбора соответствующего конструктора для целевого объекта. В вашем случае, однако, файл является Lvalue, и ни один из случаев сверху не применяется. Вам нужно будет использовать явный ход.

К сожалению, С++ 11 не имеет синтаксиса для "захвата перемещения". ИМХО, это позор. Но std:: bind поддерживает это. Должно быть возможно совместить std:: bind с выражением лямбда следующим образом:

void foo(char const* p) {
   string s = p;
   auto fun = bind([](string const& s){
      ...
   },move(s));
   fun();
}

чтобы строка перемещалась в объект функции.

Если вы намерены вызывать эту функцию только один раз и хотите снова перенести строку из объекта функции, вы можете использовать неконстантную ссылку:

void foo(char const* p) {
   string s = p;
   auto fun = bind([](string & s) {
      some_other_func(move(s));
   },move(s));
   fun();
}

Обратите внимание, что если вы не хотите использовать bind здесь, но пусть конструктор лямбда-объекта создает копию s, для перемещения строки из объекта функции требуется ключевое слово mutable:

void foo(char const* p) {
   string s = p;
   auto fun = [=]() mutable {
      //            ^^^^^^^
      some_other_func(move(s));
   };
   fun();
}

поскольку в противном случае функция operator() типа замыкания будет const-qual, что, в свою очередь, делает s строкой с константой.

В С++ 14 предложение lambda capture стало немного более гибким. Теперь мы можем написать

void foo(char const* p) {
   string s = p;
   auto fun = [s=move(s)]() mutable { // #1
      some_other_func(move(s));       // #2
   };
   fun();
}

где # 1 перемещает значение строки в лямбда-объект, а # 2 перемещает значение строки (в зависимости от того, как some_other_func объявлено точно).