Lambda expressions
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
}
$ g++ --version
g++ (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch revision 160292]
$ g++ -std=gnu++0x l.cpp
$ ./a.out
0 1 2 3 4 5 6 7 8 9
$
[] lambda-introducer
(int n) lambda parameter declaration
{ ... } compound statement
Lambda expressons define classes and construct objects.
The program above is equivalent to:
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
struct LambdaFunctor
{
void operator() (int n) const { cout << n << " "; }
};
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
for_each(v.begin(), v.end(), LambdaFunctor());
cout << endl;
return 0;
}
The expression can contain multiply statements
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
for_each(v.begin(), v.end(), [](int n) {
cout << n ;
if ( n % 2 )
cout << ":odd ";
else
cout << ":even ";
});
cout << endl;
return 0;
}
0:even 1:odd 2:even 3:odd 4:even 5:odd 6:even 7:odd 8:even 9:odd
If the lambda expession has return value, than the return type
is automatically deduced from that expression:
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
deque<int> d;
transform(v.begin(), v.end(), front_inserter(d), [](int n) {return n*n;} );
for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
}
81 64 49 36 25 16 9 4 1 0
Sometimes it is not easy to deduce the return type. We can explicitly
specify the return type.
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
#include <deque>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
deque<double> d;
transform(v.begin(), v.end(), front_inserter(d),
[](int n) -> double { return n / 2.0; } );
for_each(d.begin(), d.end(), [](double n) { cout << n << " "; });
cout << endl;
return 0;
}
4.5 4 3.5 3 2.5 2 1.5 1 0.5 0
-> double is the lambda return type clause
(it is on the right, because [] should be on left for parsing)
[] means, that the lambda is "stateless", but we can "capture" local variables
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int x = 0;
int y = 0;
cin >> x >> y;
v.erase( remove_if(v.begin(),
v.end(),
[x,y](int n) { return x < n && n < y; }
),
v.end()
);
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
}
3 6
0 1 2 3 6 7 8 9
We passed the x and y parameters by value. Basically it is equivalent:
struct LambdaFunctor
{
public:
LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
bool operator()(int n) const { return m_a < n && n < m_b; }
private:
int m_a;
int m_b;
};
// ...
v.erase( remove_if(v.begin(),v.end(),LambdaFunctor(x,y)), v.end());
The x and y parameters are copied and being stored in the function object.
We cannot modify the captured values because the operator() in functor is
const.It is a "real" copy, therefore teh modification of x and y is not
reflected inside the lambda.
[=] capture-default lambda introducer. Captures all locals.
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int x = 0;
int y = 0;
cin >> x >> y;
v.erase( remove_if(v.begin(),
v.end(),
[=](int n) { return x < n && n < y; }
),
v.end()
);
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
}
3 6
0 1 2 3 6 7 8 9
Lambda expressions are const by default but we can manage to modify x and y.
iNote, that modification of prev does not update local prev variable.
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int prev = 0;
for_each(v.begin(), v.end(), [=](int& r) mutable {
const int oldr = r;
r *= prev;
prev = oldr;
});
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << "prev = " << prev << endl;
return 0;
}
0 0 2 6 12 20 30 42 56 72 prev = 0
Capture by reference
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int prev = 0;
for_each(v.begin(), v.end(), [&prev](int& r) mutable {
const int oldr = r;
r *= prev;
prev = oldr;
});
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << "prev = " << prev << endl;
return 0;
}
0 0 2 6 12 20 30 42 56 72 prev = 9
[&] Capture everything by reference
[=, &x, &y] Capture everything by value but x and y which capured by reference
[&, x, y] Capture everything by reference but x and y which capured by value
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
struct X
{
int s;
vector<int> v;
void print() const
{
for_each(v.begin(), v.end(), [](int n) { cout << n*s << " "; });
}
};
int main()
{
X x;
x.s = 2;
for(int i = 0; i < 10; ++i)
x.v.push_back(i);
x.print();
return 0;
}
$ g++ -std=gnu++0x l10.cpp
l10.cpp: In lambda function:
l10.cpp:15:60: error: ‘this’ was not captured for this lambda function
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
struct X
{
int s;
vector<int> v;
void print() const
{
for_each(v.begin(), v.end(), [this](int n) { cout << n*s << " "; });
}
};
int main()
{
X x;
x.s = 2;
for(int i = 0; i < 10; ++i)
x.v.push_back(i);
x.print();
return 0;
}
- this always captured by value
- you can implicitly capture this with [=]
We can define "nullary" lambdas
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
int i = 0;
generate_n(back_inserter(v), 10, [&] { return i++; } );
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
return 0;
}
0 1 2 3 4 5 6 7 8 9
Or you can write [&]() { return i++; }
Lambdas can be stored in tr1::functional
#include <algorithm>
#include <functional>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
//using namespace std::tr1;
void doit(const vector<int>& v, const function<void (int)>& f)
{
for_each(v.begin(), v.end(), f);
cout << endl;
}
int main()
{
vector<int> v;
int i = 0;
generate_n(back_inserter(v), 10, [&] { return i++; } );
doit(v, [](int n) { cout << n << " "; });
const function<void (int)>& ff = [](int n) { cout << n << " "; };
doit(v, ff);
return 0;
}
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9