Přeskočit na obsah

Š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.

function_template.cpp
#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(3, 7.0).

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.

non_type_params.cpp
#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)
}
Poznámka

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ý.

Úskalí

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(int, int);, 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.

explicit_instantiation.h
// ---- 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);
Klíčové poznatky
  • Š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);`?