Ce face template
Un șablon (template) este un „tipar” de cod cu tipuri ca parametri. template <typename T> T maxim(T a, T b) funcționează pentru int, double, std::string și orice tip care se poate compara. La apelul maxim(3, 7), compilatorul deduce T = int și generează funcția pentru int.
Toată biblioteca standard este făcută din șabloane: std::vector<int>, std::map<std::string, double>. Parametrii pot fi și valori constante (template <int N>), iar din C++11 pot fi în număr variabil (typename... Args).
Pentru că sunt generate la compilare, șabloanele se scriu de obicei complet în fișiere antet. Din C++20, cerințele asupra tipurilor se pot scrie clar cu concepte.
Exemple
Exemplul 1: Funcție șablon
Aceeași funcție merge pentru trei tipuri. La ultimul apel tipul este dat explicit, <double>, pentru că argumentele au tipuri diferite.
#include <print>
#include <string>
template <typename T>
T maxim(T a, T b) {
return a > b ? a : b;
}
int main() {
std::println("{}", maxim(3, 7));
std::println("{}", maxim(2.5, 1.5));
std::println("{}", maxim(std::string("Ana"), std::string("Bogdan")));
std::println("{}", maxim<double>(4, 4.5));
}
Rezultat
7
2.5
Bogdan
4.5
Exemplul 2: Clasă șablon
#include <print>
#include <string>
template <typename T>
class Pereche {
public:
Pereche(T a, T b) : primul_(a), al_doilea_(b) {}
T mai_mic() const { return primul_ < al_doilea_ ? primul_ : al_doilea_; }
private:
T primul_, al_doilea_;
};
int main() {
Pereche<int> note(9, 7);
Pereche<std::string> orase("Iași", "Arad");
std::println("Nota mai mică: {}", note.mai_mic());
std::println("Primul alfabetic: {}", orase.mai_mic());
}
Rezultat
Nota mai mică: 7
Primul alfabetic: Arad
Exemplul 3: Parametru valoare și număr variabil de argumente
N este o constantă cunoscută la compilare. suma(...) folosește o „expresie fold” (C++17) ca să adune toate argumentele.
#include <array>
#include <print>
template <int N>
std::array<int, N> primele_patrate() {
std::array<int, N> t{};
for (int i = 0; i < N; ++i) t[i] = (i + 1) * (i + 1);
return t;
}
template <typename... Numere>
auto suma(Numere... n) {
return (n + ... + 0);
}
int main() {
std::println("{}", primele_patrate<5>());
std::println("Suma: {}", suma(10, 20, 30, 40));
}
Rezultat
[1, 4, 9, 16, 25]
Suma: 100
Greșeli frecvente
- Declari șablonul într-un fișier .h și îl definești într-un .cpp. La legare primești „undefined symbol”, pentru că compilatorul nu vede definiția când generează varianta pentru tipul tău. Pune definiția în antet.
- Apelezi
maxim(4, 4.5):Tar trebui să fie șiintșidouble, deci deducerea eșuează. Dă tipul explicit (maxim<double>) sau folosește doi parametri de tip. - Folosești un tip care nu are operațiile cerute de șablon (de exemplu
maximpe o structură fără>). Eroarea poate fi foarte lungă; conceptele (C++20) o fac scurtă și clară.
Comenzi înrudite
typename: Introduce un parametru de tip într-un șablon sau anunță compilatorul că un nume care depinde de șablon este un tip.concept: Dă un nume unui set de cerințe pentru tipuri, folosit ca să restrângi ce tipuri acceptă un șablon.requires: Scrie cerințe pentru parametrii unui șablon: ca clauză care restrânge șablonul sau ca expresie care verifică dacă un cod este valid.if constexpr: Un if evaluat la compilare: ramura falsă este eliminată complet, deci poate conține cod care nu s-ar compila pentru tipul curent.auto: Lasă compilatorul să deducă tipul variabilei din valoarea cu care este inițializată.std::vector: Tabloul dinamic din C++: ține elementele unul după altul în memorie și își mărește singur dimensiunea.