====== 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]].