====== 6. Šablony funkcí a tříd, lambda funkce ====== Online záznam: [[https://youtu.be/uoLmlUEQEDo|YouTube]] Vzorové příklady najdete v repozitáři ''tutorials'' v adresáři tut05 git pull cd tut05 Alternativně lze najít kódy také v archivu: {{ :courses:b2b99ppc:tutorials:ppc-tut05.zip |}} ===== Šablony funkcí ===== Syntaxe šablony je následující template deklarace template void swap(T &a, T &b) { T c(a); a = b; b = c; } // pokud je možné datový typ proměnné dovodit, není třeba explicitně specifikovat int a = 10, b = 20; swap (a, b); // explicitní určení datového typu se provede parametrizací šablony (zde postrádá smysl) swap (a, b); Parametrem šablony je typ T. Klíčové slovo ''class'' značí, že parametrem bude datový typ. Datový typ nemusí být definován třídou, může to být i primitivní datový typ. Kromě ''class'' lze použít i klíčové slovo ''typename'', v tomto případě ekvivalentně((Existují případy, kdy je třeba použít explicitně jednu z těchto variant, komentář je např. na [[https://stackoverflow.com/a/2024173|Stack Overflow]].)). Název datového typu ''T'' je obvyklý, nicnméně není nijak závazný. V tomto konkrétním případu jsou tedy argumenty funkce ''swap'' reference na proměnné, jejichž hodnoty jsou v těle funkce zaměněny. Explicitní specifikace datového typu ''T'' umožňuje provést implicitní přetypování skutečných parametrů funkce (pokud je možná konverze. V následujícím příkladu si povšimněte, že funkce je kvalifikována do globálního jmenného prostoru (z důvodu zamezení konfliktu jmen) a nutnosti explicitní parametrizace šablony. template T max ( T x, T y ) { return x > y ? x : y; } std::cout << ::max (10, 'b') << std::endl; // zobrazeno b std::cout << ::max (10, 'b') << std::endl; // zobrazeno 98 std::cout << ::max (10, 'b') << std::endl; // deduced conflicting types for parameter 'T' ('int' and 'char') Parametrem šablony nemusí být jen jeden datový typ: template T1 min (T1 a, T2 b) { return (a <= b)? a : b; } Pokud chceme v šabloně použít vlastní datový typ, definovaný např. třídou, je třeba přetížit všechny operátory, které jsou v šabloně použity. Pro některé argumenty funkce nemusí dávat smysl, např. v tomto případě nechceme porovnávat ukazatele, ale dereferencované hodnoty. V tom případě je možné vytvořit přetíženou funkci s explicitním uvedením datových typů argumentů. int min (int *a, int *b) { return (*a <= *b) ? *a : *b; } ===== Šablony tříd ===== Šablony tříd jsou přirozeným rozšířením šablon funkcí. Již jsme se s nimi setkali v případě STL, kde je možné parametrem šablony specifikovat datový typ prvků kontejneru. Typickým příkladem může být šablona zásobníku, který bude ukládat položky datového typu ''T''. template Stack { T* items; public: Stack(int size = 10); void Push(T x); T Pop(); }; Stack a; // zásobník o defaultní velikosti 10 prvků Stack b(5); // zásobník o velikost 5 prvků Velikost zásobníku byla v předcházejícím kódu řešena pomocí parametru zásobníku. Je ovšem možné pro tento účel použít také druhý parametr šablony template Stack { T items[size]; public: void Push (T x); T Pop(); }; ===== Lambda funkce ===== Lambda funkce (výraz) zavádí do C++ možnost velmi elegantně nahradit jednoduché funkce. Obvykle se jedná o operace jako je porovnání prvků, provedení určité matematické operace, atp. Lambda výraz má následující podobu: [zachytávané proměnné](seznam formálních parametrů) -> návratový typ {tělo lambda výrazu}(skutečné parametry) Vytvořme lambda výraz, který provede součet dvou čísel: [](int a,int b)->int{return a+b;}(2,4); Výsledek lambda výrazu může být dále využit, např. přiřazen do proměnné. Pro opakované využití lambda výrazu lze vytvořit ukazatel na lambda výraz a opakovaně jej volat. Ukazatel bude datového typu [[http://www.cplusplus.com/reference/functional/function/function/|std::function]], nebo možné využít pro zjednodušení deklarace klíčové slovo ''auto''. auto func=[](int a,int b)->int{return a+b;}; std::cout << func(2,3) << std::endl; Zatímco v předchozích příkladech byly skutečné parametry předány lambda výrazu explicitně, v případě použití lambdy jako náhrady funktoru mohou být předávány implicitně; např. v případě funkce [[http://www.cplusplus.com/reference/algorithm/sort/|std::sort]] jsou předávány hodnoty dvou po sobě následujících prvků v tříděné kolekci. bool compare(int i,int j){ return (i a; // seřazení vektoru vzestupně pomocí funkce std::sort (a.begin(), a.end(), compare); // seřazení vektoru sestupně pomocí lambda funkce std::sort (a.begin(), a.end(), [](int a, int b){return a > b;}); Při implicitním předávání skutečných parametrů funkci je možné pro další parametrizaci funkce využít zachytávání proměnných platných v nadřazené funkci (bloku), jejichž seznam je uveden v hranatých závorkách na začátku lambda výrazů. Dají se zachytávat následovně: * ''[a, &b]'' - proměnná ''a'' je zachycena hodnotou jako konstanta, proměnná ''b'' referencí, je ji tedy možné ovlivňovat operací v lambda výrazu * ''[&]'' - všechy proměnné platné v nadřazeném bloku jsou zachyceny jako reference * ''[=]'' - všechy proměnné platné v nadřazeném bloku jsou zachyceny jako hodnoty * ''[=, &x, &y]'' – zachycení všech proměnných hodnotou kromě ''x'' a ''y'', které jsou odkazem * ''[&, x]'' – zachycení všech proměnných převzetí všech odkazem kromě ''x'', které je hodnotou * ''[this]'' - zachycení ukazatele ''this'' platného v dané instanci Příklad generování hodnot vektoru pomocí parametrizovaného lambda výrazu. Všimněte si, že inkrementace manipuluje proměnnou ''index'', proto je třeba ji předat jako referenci. V případě zachycení proměnné hodnotou je proměnná konstantou, přístup je možné modifikovat pomocí ''mutable''. std::vector a(10); int index = 0; generate(a.begin(), a.end(), [&index]{return ++index;}); Proměnnou pro parametrizaci je možné definovat v hranatých závorkách. Pokud má být takto definovanou proměnnou manipulováno, je třeba použít ''mutable'' std::vector a(10); generate(a.begin(), a.end(), [&index]() mutable {return ++index;}); Více o lambda výrazech lze najít např. [[https://en.cppreference.com/w/cpp/language/lambda|zde]].