8. Funkcje

8.6. Wyrażenia lambda

Wyrażenia lambda nie były dostępne w języku C++ od samego początku – pojawiły się w momencie definicji standardu C++11 – dlatego też są mniej znane i wykorzystywane niż na to zasługują. Do czego właściwie służą takie wyrażenia? 

 Ich podstawowe zastosowanie to tworzenie anonimowych funkcji. Jeśli ktoś z Was zna choćby podstawy JavaScript to wie, jak takie funkcje bez nazwy mogą być przydatne - np do budowy tzw callback-ów (wywołań zwrotnych). 

 Funkcja anonimowa to funkcja która nie posiada nazwy, lecz posiada swoje ciało, i można ją wywołać. Zasady tworzenia ciała funkcji lambda, jak i wszelkie ograniczenia kodu tam zamieszczanego są dokładnie takie same jak w przypadku standardowych funkcji C++ - cała różnica opiera się na nazwie, a dokładniej – jej braku. Więc dlaczego to takie użyteczne? Bo wygodne ... ;>

Sama składnia wyrażenia lambda jest następująca:


[]//początek wyrażenia lambda
(/*definicje argumentów*/)->/*typ wartości zwracanej*/
{
 //ciało funkcji
}(Wartości zwracanych argumentów)

Przykładowe wyrażenie może więc wyglądać następująco:


// wyrażenie lambda
[]()->double {return 3.14; }();
/// odpowiadający mu kod klasyczny:
double dajPi() { return 3.14; }
dajPi();

Oczywiście – jeśli wyrażenie lambda zwraca jakąś wartość – to możemy ją odebrać, podobnie jak w przypadku zwykłych funkcji:


double pi = []()->double {return 3.14; }();

No dobrze – to gdzie ta wygoda, pomijając krótszy zapis jak na razie? Wygoda pojawia się, jak zaczniemy korzystać z adresów takiego wyrażenia. Adres możemy uzyskać pomijając nawiasy z wartościami parametrów w definicji wyrażenia. Przykład z dodawaniem dwóch liczb:


auto pAdres =[]( int x, int y )->int { return x+y; };
cout << pAdres( 5, 6 ) << pAdres( 7, 8 );
return 0;

Ciągle nie do końca widać tą wygodę. Ale ... przejdźmy do funkcji ogólnych, takich jak sort z biblioteki standardowej. Funkcja sort przyjmuje jako parametr wskaźnik do funkcji porównującej dwa pozostałe argumenty. Tą funkcję porównującą zawsze do tej pory musieliśmy definiować – wymyślać jej nazwę, zaśmiecać główną przestrzeń nazw, itp ... Teraz można podać w to miejsce wyrażenie lambda:


#include "cstdio"
#include "vector"
#include "ctime"
#include "cstdlib"
#include "algorithm"

int main()
{
    std::srand( std::time( NULL ) );
    typedef std::vector < int > VDaneT;
    VDaneT dane( 10 );
    for( size_t i = 0; i < dane.size(); ++i )
         dane[ i ] = rand() % 50 + 1;

    std::sort( dane.begin(), dane.end(),[]( const int & a, const int & b )->bool { return a > b; } );

    for( size_t i = 0; i < dane.size(); ++i )
         std::printf( "%d, ", dane[ i ] );

    return 0;
}

Teraz mam nadzieję, iż widać że wygodnie ;)