« Wyrażenia lambda (C++11), lekcja »
Po przeczytaniu niniejszego rozdziału powinieneś wiedzieć co to są wyrażenia lambda, jak się ich używa oraz jakie było praktyczne uzasadnienie ich wprowadzenia do standardu C++11. (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: Piotr Szawdyński
Kurs C++

Wyrażenia lambda (C++11)

[lekcja] Po przeczytaniu niniejszego rozdziału powinieneś wiedzieć co to są wyrażenia lambda, jak się ich używa oraz jakie było praktyczne uzasadnienie ich wprowadzenia do standardu C++11.
Wraz z ukazaniem się nowego standardu C++, oznaczonego jako C++11 pojawiły się nowe usprawnienia języka. Jednym z nich są tzw. wyrażenia lambda, którym poświęcę niniejszy rozdział.

Do czego służą wyrażenia lambda?

Wyrażenia lambda umożliwiają tworzenie anonimowych funkcji, czyli funkcji które posiadają ciało lecz nie posiadają swojej nazwy. Funkcje utworzone za pomocą wyrażenia lambda można definiować jak również wywoływać. Zapisy w ciele funkcji stworzonej przy pomocy wyrażenia lambda stosuje się dokładnie takie same jak w tradycyjnych funkcjach C++. O co więc tyle szumu? Tak naprawdę o wygodę programisty - jednak zanim zaczniesz tej wygody czerpać korzyści to najpierw musisz nauczyć się ich składni (oraz posiadać kompilator, który obsługuje standard C++11).

Składnia wyrażenia lambda

Wiemy już czym są wyrażenia lambda - zobaczy teraz jak wygląda składnia do zapisywania wyrażenia lambda:
C/C++
[] //informujesz, że zaczyna się wyrażenie lambda
( /*definiujesz argumenty funkcji*/ )->/*tu określasz zwracany typ*/
{
    //ciało funkcji (zwane także ciałem wyrażania lambda)
}( /*wartości argumentów z jakimi wyrażenie lambda ma zostać wywołane*/ )

Wyrażenia lambda w praktyce

Przełóżmy teorię na praktyczne zastosowanie wyrażeń lambda:
C/C++
[]()->int { return 123; }();
Powyższy zapis jest równoważny zapisowi:
C/C++
int funkcja()
{
    return 123;
}
//...
funkcja();
Wartość jaką zwraca wyrażenie lambda możemy oczywiście zapisać - robi się to tak samo jakbyś to zrobił w przypadku standardowej funkcji C++:
C/C++
int iMojaZmienna =[]()->int { return 123; }();
Wynik wyrażenia lambda można również od razu podstawić jako argument innej funkcji:
C/C++
std::printf( "Wynik wyrazenia lambda: %d\n",[]()->int { return 123; }() );
Jak widać cała filozofia z wyrażeniami lambda sprowadza się do zapisu.

Przekazywanie argumentów do wyrażenia lambda

Wyrażenia lambda jak już wspomniałem na początku są dokładnie tym samym co funkcje - różnią się jedynie swoim zapisem oraz tym, że nie posiadają one swej nazwy. Skoro wyrażenie lambda to nic innego niż funkcja to nic nie stoi na przeszkodzie by funkcja miała również argumenty. Argumenty wyrażeniu lambda nadaje się w pierwszych nawiasach okrągłych. Wartości jakie mają przyjąć poszczególne argumenty określa się natomiast między okrągłymi nawiasami, które kończą wyrażenie lambda. Przykład:
C/C++
std::printf( "Wynik wyrazenia lambda: %d\n",[]( int a, int b )->int { return a * b; }( 5, 6 ) );

Zwracana wartość przez wyrażenie lambda

Typ zwracanej wartości w wyrażeniu lambda określa się po symbolu strzałki (
->
). Wyrażenie lambda może zwracać dowolną wartość, którą potrafi zwrócić tradycyjna funkcja C++. Może być to również typ void:
C/C++
[]( int x, int y )->void { std::printf( "Wyrazenie lambda: %d + %d = %d\n", x, y, x + y ); }( 5, 6 );

Adres funkcji na którą wskazuje wyrażenie lambda

Jak już zapewne zauważyłeś po dotychczasowych przykładach - wyrażenia lambda to nic innego jak funkcje, a skoro tak to fajnie będzie, jeżeli będziemy mieli możliwość pobrania adresu do funkcji, którą reprezentuje wyrażenie lambda. Aby uzyskać adres funkcji zapisanej przy pomocy wyrażenia lambda wystarczy pominąć nawiasy w których określa się wartości argumentów z jakimi wyrażenie lambda ma zostać wywołane. Oto przykład, który zapisuje adres funkcji do zmiennej pAdres, a następnie wywołuje dwukrotnie wyrażenie lambda poprzez adres zapisany w zmiennej:
C/C++
#include <cstdio>

int main()
{
    auto pAdres =[]( int x, int y )->void { std::printf( "Wyrazenie lambda: %d + %d = %d\n", x, y, x + y ); };
    pAdres( 5, 6 );
    pAdres( 7, 8 );
    return 0;
}
Standardowe wyjście programu:
Wyrazenie lambda: 5 + 6 = 11
Wyrazenie lambda: 7 + 8 = 15

Wyrażenia lambda i ich praktyczne zastosowanie

Do tej pory otrzymałeś masę informacji na temat wyrażeń lambda, a konkretniej jak z nich korzystać oraz co one umożliwiają. Teraz zademonstruję Ci na przykładzie jakie były prawdziwe intencje autorów standardu C++11 aby wprowadzić wyrażenia lambda do języka C++:
C/C++
#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;
}
Bez wyrażenia lambda bylibyśmy zmuszeni do utworzenia funkcji tylko i wyłącznie po to, aby posortować dane w sposób jaki nas interesuje. Ten sam kod bez wyrażeń lambda wyglądałby tak:
C/C++
#include <cstdio>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <algorithm>

bool porownaj( const int & a, const int & b )
{
    return a > b;
}

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(), porownaj );
   
    for( size_t i = 0; i < dane.size(); ++i )
         std::printf( "%d, ", dane[ i ] );
   
    return 0;
}

Podsumowanie

Niniejszy rozdział dobiegł końca. W tym miejscu jeszcze wspomnę, że nie zostały tu przedstawione wyczerpujące informacje na temat wyrażeń lambda jakie wynikają ze standardu C++11. Mam nadzieję, że znajomość podstaw wyrażeń lambda zmotywuje Cię do lepszego zapoznania się ze standardem C++11 oraz poszerzeniem wiedzy z ich zakresu.

Przydatne linki

Poprzedni dokumentNastępny dokument
WskaźnikiZarządzanie pamięcią new, delete