Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving with lambdas

Tags:

c++

c++11

lambda

When using lambda functions, let's say that you decide to copy a variable (with the [=] notation). If you never reference that variable again, is the compiler allowed to move it into the resultant function object?

Edit: For example, I've wrote a snippet to move calls across threads. Here's a sample that does so.

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

But clearly, the file variable doesn't need to live past the lambda definition- and indeed, the lambda is only called once, so I moved the copy.

like image 422
Puppy Avatar asked Dec 13 '10 20:12

Puppy


1 Answers

If you never reference that variable again, is the compiler allowed to move it into the resultant function object?

No. The only situation where the compiler is allowed to replace a copy with a move are the exact same situations where it is allowed to perform a copy elision. These situations include returning a local object by value or initializing an object with a temporary. In these cases the compiler is allowed to elide the copy by making source and target the same object. If the compiler is not able to do that for whatever reason it has to consider the source object as an rvalue with respect to overload resolution for selecting the appropriate constructor for the target object. In your case, however, file is an Lvalue and none of the cases from above apply. You would have to use an explicit move.

Unfortunately, C++11 doesn't have a syntax for a "move capture". IMHO, it's a shame. But std::bind supports this. It should be possible to combine std::bind with a lambda expression like this:

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

so that the string is moved into the function object.

If you intent to call this function only once and want to move the string out of the function object again, you can use a non-const reference:

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

Note that, if you don't want to use bind here but let the lambda object's constructor create a copy of s, moving the string out of the function object requires the mutable keyword:

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

because otherwise the closure type's operator() function will be const-qualified which in turn makes s a const-qualified string.

In C++14 the lambda capture clause got a little more flexible. We can now write

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

where #1 moves the string value into the lambda object and #2 moves the string value out (depending on how some_other_func is declared exactly).

like image 79
sellibitze Avatar answered Oct 22 '22 18:10

sellibitze