Moving with lambdas
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, add开发者_JAVA技巧ed);
});
}
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.
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).
Update C++14: It is possible with initialised captures (added with this version):
[x = std::move(y)]() { /* ... */ }
Though with variadic templates I only managed to get the appropriate effect via a std::tuple
and std::apply
(if I missed some syntactic sugar for appreciate a hint...):
template <typename ... T>
void f(T&& ... t)
{
[x = std::tuple<T...>(std::move(t) ...)]()
{
std::apply([&](auto const& ... t) { /* ... */ }, x);
}
}
Demonstration on godbolt – note how the strings get emptied illustrating how their contents are moved into x
or into the tuple.
精彩评论