As seen in the question lambda capture by value mutable doesn't work with const &?, when capturing a value of type const T& using its name or [=] in a mutable lambda, the field in the hidden class gets type const T. It could be argued for that this is the correct thing to do for mutable lambdas.
But why is this done for non-mutable lambdas too? In non-mutable lambdas, the operator()(...) is declared const, so it can't modify captured values anyway.
The bad consequences of this happen when we move the lambda, for example when wrapping it in an std::function.
See the following two examples:
#include <cstdio>
#include <functional>
std::function<void()> f1, f2;
struct Test {
Test() {puts("Construct");}
Test(const Test& o) {puts("Copy");}
Test(Test&& o) {puts("Move");}
~Test() {puts("Destruct");}
};
void set_f1(const Test& v) {
f1 = [v] () {}; // field type in lambda object will be "const Test"
}
void set_f2(const Test& v) {
f2 = [v = v] () {}; // field type in lambda object will be "Test"
}
int main() {
Test t;
puts("set_f1:");
set_f1(t);
puts("set_f2:");
set_f2(t);
puts("done");
}
We get the following compiler-generated lambda classes:
class set_f1_lambda {
const Test v;
public:
void operator()() const {}
};
class set_f2_lambda {
Test v;
public:
void operator()() const {}
};
The program prints the following (using gcc or clang):
Construct
set_f1:
Copy
Copy
Copy
Destruct
Destruct
set_f2:
Copy
Move
Move
Destruct
Destruct
done
Destruct
Destruct
Destruct
The v value is copied not less than three time in the first example set_f1.
In the second example set_f2, the only copy is when capturing the value (as expected). The fact that two moves are used is an implementation detail in libstdc++. The first move happens internally in operator= in std::function when passing the functor by value to an internal function (why doesn't this function signature use pass-by-reference?). The second move happens when move constructing the final heap-allocated functor.
The move constructor of the lambda functor object however, can't use a move constructor for a field if the field is const (since such a constructor can't "clear out" a const variable after stealing its content). That's why the copy constructor must be used instead for such fields.
So to me it seems to only have negative impact to capture values as const in non-mutable lambdas. Did I miss something important, or has it simply been standardised this way to make the standard more simpler somehow?