与lambdas一起移动

时间:2010-12-13 20:20:27

标签: c++ lambda c++11

使用lambda函数时,假设您决定复制变量(使用[=]表示法)。如果您再也没有引用该变量,是否允许编译器将其移动到结果函数对象中?

编辑:例如,我编写了一个代码片段来跨线程移动调用。这是一个样本。

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

但显然,文件变量不需要经过lambda定义 - 事实上,lambda只被调用一次,所以我移动了副本。

1 个答案:

答案 0 :(得分:12)

  

如果再也没有引用该变量,是否允许编译器将其移动到结果函数对象中?

没有。允许编译器用移动替换副本的唯一情况是允许执行复制省略的完全相同的情况。这些情况包括按值返回本地对象或使用临时初始化对象。在这些情况下,允许编译器通过使源和目标成为相同的对象来消除副本。如果编译器由于某种原因无法执行此操作,则必须将源对象视为关于重载决策的rvalue,以便为目标对象选择适当的构造函数。但是,在您的情况下,file是Lvalue,并且上述情况均不适用。你必须使用明确的举动。

不幸的是,C ++ 11没有“移动捕获”的语法。恕我直言,这是一种耻辱。但是std :: bind支持这个。应该可以将std :: bind与lambda表达式结合使用,如下所示:

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

以便将字符串移动到函数对象中。

如果您只打算调用此函数一次,并希望再次将该字符串移出函数对象,则可以使用非const引用:

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

注意,如果你不想在这里使用bind但是让lambda对象的构造函数创建s的副本,那么将字符串移出函数对象需要mutable关键字:

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

因为否则闭包类型的operator()函数将是const限定的,这反过来使s成为一个const限定的字符串。

在C ++ 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将字符串值移动到lambda对象中,#2将字符串值移出(取决于some_other_func的确切声明方式)。