[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