[capture list] (parameter list) -> return type { function body }
- caputre list - najczęściej pusta, zawiera listę lokalnych zmiennych
- parameter list - można ominąć. Wygląda tak jak w przypadku zwykłych funkcji. Nie ma tu czegoś takiego jak argumenty domyślne
- return type - można ominąć. Wygląda tak jak w przypadku zwykłych funkcji, jednak cała deklaracja lambda musi korzystać z konstrukcji "trailing return"
- function body - wygląda tak jak w przypadku zwykłych funkcji
- [ ] - lista pusta, lambda nie może używać zmiennych z głównej funkcji
- [name1, name2] - nazwy zmiennych oddzielone przecinkami, które będą wykorzystywane w lambdzie (domyślnie wartości są kopiowane), jeżeli jakaś nazwa zostanie poprzedzona &, lambda dostanie ją przez referencję
- [&] - przed domniemanie (implicit), wszystkie zmienne z głównej funkcji będą dostępne przez referencję
- [=] - przed domniemanie (implicit), wszystkie zmienne z głównej funkcji będą dostępne przez kopię. Jeżeli lambda jest wewnątrz metody to od C++20 jest to przestarzały zapis i powinien być zastąpiony przez [=, this]
- [&, id1, id2] - zmienne id1, id2 będą dostępne przez kopię (nie poprzedzać żadnej &!!!), wszystkie inne będą domyślnie dostępne przez referencję
- [=, &ref1, &ref2] - zmienne ref1 i ref2, będą dostępne przez referencję, wszystkie inne będą domyślnie dostępne przez kopię
- [this], [&, this], [=, this], [&] - this (od C++17) będzie dostępne przez referencję.
- [*this], [&, *this], [=, *this] - this (od C++17) będzie dostępne przez kopię
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> vec = {1, 4, 7, 2, 3};
int evens = std::count_if(begin(vec), end(vec),
[](int value) { return value % 2 == 0; });
cout << "Parzystych: " << evens << endl;
return 0;
}
Wynik: Parzystych: 2Drugi przykład trochę bardziej rozbudowany. Zliczane są elementy powyżej pewnego progu (threshold), a wynik zapisywany jest do zmiennej znajdującej się w głównej funkcji.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vec = {1, 4, 7, 2, 3};
int count_above = 0;
int threshold = 3;
std::for_each(begin(vec), end(vec),
[&, threshold](int &v) { if(v > threshold) count_above++; });
std::cout << "Values above(" << threshold << "): " << count_above << std::endl;
return 0;
}
Wynik: Values above(3): 2Jeżeli chcemy zmienić wartość przechwyconej zmiennej musimy po "parameter list" dodać słowo kluczowe mutable. Ale wartość ta będzie zmieniona tylko w obrębie samej lambdy! Poniżej link, z dobrym wyjaśnieniem tego mechanizmu:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> vec = {1, 2, 3, 4};
int count = 1;
auto change_second = [=](int v) mutable {
if (count == 2)
v = 222;
count++;
return v;
};
cout << "count berfore: " << count << endl;
std::transform(begin(vec), end(vec), begin(vec), change_second);
cout << "count after: " << count << endl;
cout << "New vector values: ";
for(int& v : vec)
cout << v << " ";
return 0;
}
Wynik (jak się zaczyna zabawę z lambdami, trochę zaskakujący): count berfore: 1 count after: 1 New vector values: 1 222 3 4Standard nie definiuje w jaki sposób funkcja lambda zostanie zaimplementowana. Ta sama lambda, w dwóch implementacjach może być dwoma zupełnie innymi typami. Do przechowywania można wykorzystać std::function, wskaźnik do funkcji lub wykorzystać detekcję tupu za pomocą auto. I właśnie korzystanie z auto jest zalecanym sposobem. std::function, może wołać lambdę, za pośrednictwem funkcji wirtualnych, co wpływa na szybkość działania programu (mechanizm type-erasure - nie weryfikowałem tego).
To czego zabrakło w C++11, a zostało naprawione w C++17, czyli obsługa this.
#include <iostream>
#include <vector>
using namespace std;
struct MyClass {
MyClass() : vec{0, 0, 0} {
}
void update() {
auto fun = [&, this]() { this->vec[1] = 9; };
fun();
}
vector<int> vec;
};
int main()
{
MyClass obj;
obj.update();
for (const auto& v : obj.vec)
cout << v << " ";
}
Wynik:0 9 0Oczywiście sprawa się na tym nie kończy. Istnieją duża bardziej skomplikowane przypadki np. zwracanie lambdy modyfikująca pola obiektu z metody tego obiektu. Dobry opis na tym blogu [2].
Brak komentarzy:
Prześlij komentarz