I've got a quick test below:
#include<iostream>
using namespace std;
int main(){
int i=2;
auto f=[=]()mutable{++i;};
f();
f();
cout<<i<<endl;
return 0;
}
But the result it still prints "2". Why i is not modified inside a mutable lambda? I'm using clang --std=c++1z.
Thanks!
You use [=] for your capture which means the lambda gets a copy of i. This copy is independent from the i in main.
What you need to do is capture by reference [&] in order to apply the changes to the i in main.
int i=2;
auto f=[=]()mutable{++i;};
f();
f();
std::cout<<i<<std::endl;
this prints 2.
int i=2;
auto f=[&](){++i;};
f();
f();
std::cout<<i<<std::endl;
this prints 4.
int i=2;
auto f=[=]()mutable{++i; std::cout << i << std::endl;};
f();
f();
std::cout<<i<<std::endl;
this prints 3 4 2.
= copies captured data into the lambda.
If mutable the copies can be modified.
& references captured data in the lambda.
Modifying things through references is legal.
[=] is the same as [i], and [&] is the same as [&i] in this context (you can explicitly list captures, or let them be captured implicitly by listing none and using = or &).
[=] means you're capturing i by value. Even the mutable lambda could modify it but it's just a copy inside the lambda, then any modification on it would have nothing to do with the original variable.
You might want capture-by-reference, and then mutable is not needed again. e.g.
auto f = [&i]() {++i;};
Related
This question already has answers here:
Implicit type in lambda capture
(2 answers)
Closed last year.
Why is the variable 'n' in 2nd usage of std::generate and within lambda capture not preceded with it's data type in below code?
I thought it's important to specify the datatype of all identifiers we use in a c++ code.
#include <algorithm>
#include <iostream>
#include <vector>
int f()
{
static int i;
return ++i;
}
int main()
{
std::vector<int> v(5);
auto print = [&] {
for (std::cout << "v: "; auto iv: v)
std::cout << iv << " ";
std::cout << "\n";
};
std::generate(v.begin(), v.end(), f);
print();
// Initialize with default values 0,1,2,3,4 from a lambda function
// Equivalent to std::iota(v.begin(), v.end(), 0);
std::generate(v.begin(), v.end(), [n = 0] () mutable { return n++; });
print();
}
Why is the variable 'n' in 2nd usage of std::generate and within lambda capture not preceded with it's data type in below code?
It's not preceded with a data type, because the grammar of C++ says that it doesn't need to - nor even allowed to - be preceded by a type name.
A type name isn't needed because the type is deduced from the type of the initialiser expression.
From cppreference:
A capture with an initializer acts as if it declares and explicitly captures a variable declared with type auto, whose declarative region is the body of the lambda expression (that is, it is not in scope within its initializer), [...]
Lambdas used the opportunity of a syntax that was anyhow fresh and new to get some things right and allow a nice and terse syntax. For example lambdas operator() is const and you need to opt-out via mutable instead of the default non-const of member functions.
No auto in this place does not create any issues or ambiguities. The example from cppreference:
int x = 4;
auto y = [&r = x, x = x + 1]()->int
{
r += 2;
return x * x;
}(); // updates ::x to 6 and initializes y to 25.
From the lambda syntax it is clear that &r is a by reference capture initialized by x and x is a by value capture initialized by x + 1. The types can be deduced from the initializers. There would be no gain in requiring to add auto.
In my experience n could have been just declared inside the lambda body with auto or int as datatype. Isnt it?
Yes, but then it would need to be static. This produces the same output in your example:
std::generate(v.begin(), v.end(), [] () mutable {
static int n = 0;
return n++; });
However, the capture can be considered cleaner than the function local static.
In C++11 (from cppreference.com):
[&] captures all automatic variables used in the body of the lambda by
reference and current object by reference if exists
And...
[a,&b] where a is captured by copy and b is captured by reference
So my question is, if we have a class such as (VERSION A):
class Foo
{
public:
void test()
{
auto y = [&](){ return x; }(); // Line 6
}
int x;
};
In Line 6, we successfully capture the member variable x using the "all automatic variables by reference" capture specifier.
Or we can write (VERSION B):
{
int& x = this->x;
auto y = [&x](){ return x; }();
}
But the following does not compile (VERSION C):
{
auto y = [&x](){ return x; }();
}
This is because x does not exist as a name in the enclosing scope.
This also does not compile (VERSION D):
{
auto y = [&this](){ return x; }();
}
This is because this cannot be captured by reference.
So, my question is, why does VERSION A work and not VERSION C or VERSION D? I understand why VERSION C and D do not work, but I don't understand why A works.
If we can't capture this by reference, and we cannot capture variables not in the parent scope, how is x being captured in VERSION A?
Intuitively, according to the capture rules VERSION B is the only version I'd expect to work.
Version A works because *this is implicitly captured by reference.
The current object (*this) can be implicitly captured if either capture default is present.
When you specify capture default (the [&] in this case), *this is implicitly captured; which has the same effect as [this]. Note that what's captured by reference is *this, not this itself; this can't be captured explicitly by reference like [&this], so version D fails.
Every now and then I come up with code like this:
// lazy lambda:
[&] {
// use references to a, b, c
}
// pedantic lambda
[&a, &b, &c] {
// use references to a, b, c
}
I'd like to know which of the lambdas is better in terms of performance and perhaps executable size according to the C++14 standard (or later) and your practical experience with compilers.
There is no difference in this example. The compiler will only capture variables that are explicitly referenced in the lambda, making the lazy capture equivalent to the explicit one.
A lazy capture is slightly more convenient. Changing the body of the lambda to use additional captured variables, or removing all references to a captured variable from an existing lambda, would normally also require updating an explicit capture list. With a lazy capture the compiler will do everything for you.
Excluding the trivial case where you explicitly capture something that isn't mentioned in the lambda at all, there are two corner cases where there might be a difference.
First, implicit captures generally do not capture entities that aren't odr-used (but see the next item for the exception to this). This includes, among other things, things mentioned in unevaluated operands such as those of decltype and sizeof, as well as certain const and constexpr local variables when used in certain contexts (consult [basic.def.odr] for the full set of rules):
void f(int){}
void f2(const int &) {}
void t() {
const int x = 8;
constexpr double y = 8;
const double z = 8;
auto g = []{ f(x); }; // OK
auto g1 = [=]{ f(x); }; // OK, does not capture x
// usually won't fire, though not guaranteed
static_assert(sizeof(g) == sizeof(g1), "!!");
auto g2 = []{ f(y); }; // OK
auto g3 = []{ f(z); }; // Error, must capture z
auto g4 = []{ f2(x); }; // Error, must capture x since it's odr-used
auto g5 = [=]{ f2(x); }; // OK, captures x
auto g6 = []{ f2(+x); }; // OK, doesn't odr-use x
auto g7 = []{ f2(y); }; // OK
}
If you write the capture list manually, it's possible that you'll capture more than you technically need to, because the rules governing what is or isn't odr-used are pretty complex.
Second, implicit captures in generic lambdas will capture things used in a dependent expression, even when they aren't necessarily odr-used, for sanity. Borrowing an example from the standard:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
// Will actually odr-use x only if sizeof(a) == 1
};
}
However, you are not actually required to capture something that may-or-may-not-be-odr-used when writing a generic lambda; you are only required to capture it if you instantiate a specialization of the function call operator that odr-uses the thing. And so if you never call the resulting generic lambda in a way that would odr-use the thing at issue, then the implicit capture may capture more than the minimum you need. For example, this is allowed:
void test() {
const int x = 17;
auto g3 = [](auto a) { // no capture
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK for now
};
}
as long as you don't call g3 with anything whose size is 1: g3(0) is OK on typical systems; g3('\0') isn't.
Like people said above, there will be no difference in regards to generated code. However, I believe, it is better to be explicit - it gives better readability (easier to reason about variable life cycle) and also gives you better support from the compiler, producing errors when you access the variable you didn't mean to.
I believe 'lazy capturing' to be not a good feature at all.
I'm building a lambda function that requires access to a fair number of variables in the context.
const double defaultAmount = [&]{
/*ToDo*/
}();
I'd rather not use [=] in the list as I don't want lots of value copies to be made.
I'm concerned about program stability if I use [&] since I don't want the lambda to modify the capture set.
Can I pass by const reference? [const &] doesn't work.
Perhaps a good compiler optimises out value copies, so [=] is preferable.
You can create and capture const references explicitly:
int x = 42;
const int& rx = x;
auto l = [&rx]() {
x = 5; // error: 'x' is not captured
rx = 5; // error: assignment of read-only reference 'rx'
};
The capture list is limited in what can be captured; basically by-value or by-reference (named or by default), the this pointer and nothing.
From the cppreference;
capture-list - a comma-separated list of zero or more captures, optionally beginning with a capture-default. Capture list can be passed as follows (see below for the detailed description):
[a,&b] where a is captured by value and b is captured by reference.
[this] captures the this pointer by value
[&] captures all automatic variables odr-used in the body of the lambda by reference
[=] captures all automatic variables odr-used in the body of the lambda by value
[] captures nothing
You could create local const& to all the object you wish to capture and use those in the lambda.
#include <iostream>
using namespace std;
int main()
{
int a = 5;
const int& refa = a;
const int b = [&]() -> int {
//refa = 10; // attempts to modify this fail
return refa;
}();
cout << a << " " << b << endl;
}
The capture could be either for all the references, or an explicit list what is required;
const int b = [&refa]()
Another alternative is not to capture the local variables at all. You then create a lambda that accepts as arguments the variables you need. It may be more effort as the local variable count grows, but you have more control over how the lambda accepts its arguments and is able to use the data.
auto lambda = [](const int& refa /*, ...*/) { */...*/ }
lambda(...);
Sadly the C++11 grammar does not allow for this, so no.
You can capture a constant reference to an object, not an object itself:
A a;
const A& ref_a = a;
const double defaultAmount = [&]{
ref_a.smth();
}();
How is it possible that this example works? It prints 6:
#include <iostream>
#include <functional>
using namespace std;
void scopeIt(std::function<int()> &fun) {
int val = 6;
fun = [=](){return val;}; //<-- this
}
int main() {
std::function<int()> fun;
scopeIt(fun);
cout << fun();
return 0;
}
Where is the value 6 stored after scopeIt is done being called? If I replace the [=] with a [&], it prints 0 instead of 6.
It is stored within the closure, which - in your code - is then stored within std::function<int()> &fun.
A lambda generates what's equivalent to an instance of a compiler generated class.
This code:
[=](){return val;}
Generates what's effectively equivalent to this... this would be the "closure":
struct UNNAMED_TYPE
{
UNNAMED_TYPE(int val) : val(val) {}
const int val;
// Above, your [=] "equals/copy" syntax means "find what variables
// are needed by the lambda and copy them into this object"
int operator() () const { return val; }
// Above, here is the code you provided
} (val);
// ^^^ note that this DECLARED type is being INSTANTIATED (constructed) too!!
Lambdas in C++ are really just "anonymous" struct functors. So when you write this:
int val = 6;
fun = [=](){return val;};
What the compiler is translating that into is this:
int val = 6;
struct __anonymous_struct_line_8 {
int val;
__anonymous_struct_line_8(int v) : val(v) {}
int operator() () const {
return val; // returns this->val
}
};
fun = __anonymous_struct_line_8(val);
Then, std::function stores that functor via type erasure.
When you use [&] instead of [=], it changes the struct to:
struct __anonymous_struct_line_8 {
int& val; // Notice this is a reference now!
...
So now the object stores a reference to the function's val object, which becomes a dangling (invalid) reference after the function exits (and you get undefined behavior).
The so-called closure type (which is the class type of the lambda expression) has members for each captured entity. Those members are objects for capture by value, and references for capture by reference. They are initialized with the captured entities and live independently within the closure object (the particular object of closure type that this lambda designates).
The unnamed member that corresponds to the value capture of val is initialized with val and accessed from the inside of the closure types operator(), which is fine. The closure object may easily have been copied or moved multiple times until that happens, and that's fine too - closure types have implicitly defined move and copy constructors just as normal classes do.
However, when capturing by reference, the lvalue-to-rvalue conversion that is implicitly performed when calling fun in main induces undefined behavior as the object which the reference member referred to has already been destroyed - i.e. we are using a dangling reference.
The value of a lambda expression is an object of class type, and
For each entity
captured by copy, an unnamed non-static data member is declared in the closure type.
([expr.prim.lambda]/14 in C++11)
That is, the object created by the lambda
[=](){return val;}
actually contains a non-static member of int type, whose value is 6, and this object is copied into the std::function object.