Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

6. Šablony funkcí a tříd, lambda funkce

Online záznam: 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: ppc-tut05.zip

Šablony funkcí

Syntaxe šablony je následující

template<parametry šablony> deklarace

template<class T> 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<int> (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ě1). 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 <class T>
T max ( T x, T y ) {
	return x > y ? x : y;
}
 
std::cout << ::max<char> (10, 'b') << std::endl;  // zobrazeno b
std::cout << ::max<int> (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 <class T1, class T2>
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 <class T> Stack
{
    T* items;
 
public:
    Stack(int size = 10);
    void Push(T x);
    T Pop();
};
 
Stack<int> a;     // zásobník o defaultní velikosti 10 prvků
Stack<int> 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 <class T, int size> 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 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 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<j);
}
 
std::vector<int> 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<int> 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<int> a(10);
generate(a.begin(), a.end(), [&index]() mutable {return ++index;});

Více o lambda výrazech lze najít např. zde.

1)
Existují případy, kdy je třeba použít explicitně jednu z těchto variant, komentář je např. na Stack Overflow.
courses/b2b99ppc/tutorials/06.txt · Last modified: 2021/02/15 20:21 by viteks