Šablony funkcí a odpočet typu
Naučte se psát funkce, které fungují s jakýmkoli typem. Pochopte dedukci argumentů šablony, netypové parametry a proč šablony žijí v záhlaví.
Proč šablony funkcí?
Bez šablon znamená napsat funkci, která funguje pro více typů, duplikaci logiky. Zapsali bysteint max(int, int),double max(double, double),std::string max(std::string, std::string)— stejná logika opakující se pro každý typ. Šablony funkcí vám umožní napsat algoritmus jednou a nechat kompilátor automaticky generovat typově specifické verze.
Šablona funkce není funkce — je to návrh, ze kterého kompilátor instantuje skutečné funkce, když je používáte s konkrétními typy. To se děje zcela v době kompilace a vytváří kód identický s tím, co byste napsali ručně – nulová režie.
Syntaxe šablony
Šablona funkce je představena klíčovým slovem template následovaným seznamem parametrů v lomených závorkách. Pro parametry typu můžete zaměnitelně používat typename nebo class – znamenají přesně to samé. Moderní konvence preferujítypename.
#include <iostream>
#include <string>
// 'typename' and 'class' are interchangeable here
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Equivalent — 'class' means the same thing
template <class T>
T minimum(T a, T b) {
return (a < b) ? a : b;
}
int main() {
std::cout << maximum(3, 7) << '\n'; // deduces T = int
std::cout << maximum(3.14, 2.71) << '\n'; // deduces T = double
std::cout << maximum<std::string>("hello", "world") << '\n';
// explicit instantiation needed — "hello" is const char*, not std::string
}Pravidla pro odpočet argumentů šablony
Kompilátor odvodí argumenty šablony porovnáním typů argumentů funkcí s vzory parametrů. Klíčová pravidla jsou:
1. Preferována přesná shoda —maximum(3, 7)odvodíT = int, protože oba argumenty jsouint.
2. Žádné implicitní konverze během odpočtu —maximum(3, 7.0)je chyba: kompilátor vidíT = intz prvního argumentu aT = doublez druhého a odmítne jeden vybrat.
3. Rozpad pole na ukazatel — když se jako parametr hodnoty objevíT, pole se rozloží na ukazatele. PoužijteT(&)[N]k zachycení typu a velikosti pole.
4. Const/volatile nejvyšší úrovně je odstraněna z hodnotových parametrů, ale zachována pro parametry reference/ukazatele.
5. Sbalení referencí — je-liTodvozeno jako typ reference (v referencích pro předávání), platí pravidla pro sbalení referencí:T& &se změní naT&,T& &&se změní naT&,T&& &se změní naT&,T&& &&se změní naT&&.
Když dedukce selže nebo je nejednoznačná, musíte zadat argumenty šablony explicitně:maximum.
Netypové parametry šablony
Parametry šablony nejsou omezeny na typy. Netypové parametry šablony (NTTP) přijímají hodnoty v době kompilace, jako jsou celá čísla, ukazatele nebo (od C++20) s plovoucí desetinnou čárkou a typy tříd. Jsou výkonné pro vkládání konstant v době kompilace do typového systému.
#include <iostream>
#include <array>
#include <cstddef>
// Non-type parameter: N is a compile-time size_t value
template <typename T, std::size_t N>
T sum(const std::array<T, N>& arr) {
T total{};
for (const auto& elem : arr) {
total += elem;
}
return total;
}
// C++20: auto non-type parameter accepts any structural type
template <auto Value>
constexpr auto doubled = Value * 2;
int main() {
std::array<int, 4> nums = {1, 2, 3, 4};
std::cout << sum(nums) << '\n'; // 10, deduces T=int, N=4
std::cout << doubled<21> << '\n'; // 42
std::cout << doubled<3.14> << '\n'; // 6.28 (C++20)
}Každá jedinečná kombinace argumentů šablony způsobí, že kompilátor vygeneruje samostatnou instanci – odlišnou kopii funkce ve vašem binárním souboru. Volánímaximum,maximumamaximumvytváří tři funkce. To je obvykle v pořádku, ale ve velkých databázích kódů mohou šablony přispět k nafouknutí kódu (větší binární soubory, delší doba kompilace). Strategie zmírnění zahrnují: použití externích deklarací šablon k řízení toho, kde dochází k vytváření instancí, vyřazení typově nezávislého kódu ze šablon a použití vymazání typu, když je polymorfismus běhového prostředí přijatelný.
Protože kompilátor potřebuje vidět úplnou definici šablony, aby ji vytvořil, šablony nelze rozdělit do souborů.h/.cpptak, jak to mohou běžné funkce. Pokud vložíte definici šablony do souboru.cppa pokusíte se ji použít z jiné překladové jednotky, dostanete chybu linkeru (nedefinovaný odkaz). Standardním řešením je vložit celou definici šablony do hlavičkového souboru. Pokud se časy kompilace stanou problémem, můžete použít explicitní vytvoření instance: vložte definici do souboru.cppa přidejtetemplate int maximum, abyste explicitně vytvořili instanci specializací, které potřebujete.
Explicitní instanciace
Můžete explicitně řídit, kde a kdy se vytvoří instance šablon. To může snížit dobu kompilace ve velkých projektech tím, že se zabrání nadbytečným instancím napříč překladovými jednotkami.
// ---- maximum.h ----
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Suppress implicit instantiation in every TU that includes this header
extern template int maximum<int>(int, int);
extern template double maximum<double>(double, double);
// ---- maximum.cpp ----
#include "maximum.h"
// Explicit instantiation: generate code here, once
template int maximum<int>(int, int);
template double maximum<double>(double, double);- Šablony funkcí jsou plány – kompilátor generuje konkrétní funkce pro každou sadu argumentů šablony
typenameaclassjsou zaměnitelné v seznamech parametrů šablony- Dedukce argumentů šablony neprovádí implicitní převody – nejednoznačné dedukce jsou chyby
- Netypové parametry šablony vkládají do typového systému hodnoty v době kompilace
- Šablony musí být definovány v hlavičkách (nebo použít explicitní konkretizaci), protože kompilátor potřebuje k vytvoření instance úplnou definici
Kvíz — Otestujte své znalosti
(15 XP)1. Co se stane, když zavoláte`maximum(3, 7.0)`pomocí šablony`template<typename T> T maximum(T a, T b)`?
2. Proč musí být definice šablon obvykle umístěny v hlavičkových souborech?
3. What is the purpose of`extern template int maximum<int>(int, int);`?