Templateuri

Templateurile permit crearea funcțiilor generice, definite ca un plan sau ca o formulă, ce sunt dezvoltate pentru a funcționa cu mai multe tipuri de date diferite. Acestea se bazează pe ideea transmiterii tipului de date ca parametru, împreună cu valoarea în sine a obiectului, astfel putând scrie același cod pentru mai multe cazuri. Un exemplu este cazul unei sortări. Putem avea mai multe tipuri de obiecte pe care vrem să le sortăm, dar să le putem sorta după un parametru comun.

Exemplu

std::sort(v.begin(), v.end(), []<typename T>(const T &a, const T &b) {
    return a.ID > b.ID;
});

Fișiere .hhp

Formatul .hpp este o combinație dintre fișierele de tipul .h și cele de tipul .cpp, iar acesta este specific limbajului c++. Așa cum sugerează și numele, acesta conține atât headere cât și implementarea lor. Clasele care utilizează templateuri trebuiesc definite și implementate în fișiere .hpp, deoarece, în cazul contrat, programul nu ar compila din cauza priorității pe care o au diferite părți din cod la compilare.

Utilizare

Pentru a specifica că o clasă sau o funcție este template, trebuie decorată cu template <>, iar între <> vor fi specificate tipurile de variabile pasate mai departe, către compilator, ulterior către clasa sau funcția compilată. Folosind typename T, declarăm un tip de date de tipul T, acesta putând fi o clasă proprie, un int, un string, orice. Un template acceptă orice număr de parametrii, iar aceștia se trimit când apelăm funcția sau creăm obiectul. De asemenea, aceștia pot fi deduși și de compilator în unele cazuri. Ca o mențiune, putem scrie fie typename, fie class, dar din punctul meu de vedere, typename este mult mai general și deci mai potrivit.

Exemplu

#include <iostream>
#include <vector>

template <typename T>
T Sum(T a, T b) {
    return a + b;
}

int main() {
    std::cout << Sum<int>(4, 5) << '\n';
    std::cout << Sum(4, 5);

    std::vector <float> v;
    return 0;
}

Cum funcționează

Templateurile îi spun compilatorului ce cod să creeze la compilare pe baza apelurilor. Un template este practic un blueprint, o instrucțiune pentru compilator.

Exemplu

#include <iostream>

template <typename T>
T Sum(T a, T b) {
    return a + b;
}

int main() {
    std::cout << Sum(4, 5) << '\n';
    std::cout << Sum(4.5f, 5.6f);

    return 0;
}

În exemplul de mai sus, compilatorul își face 2 funcții, pentru fiecare apel cu un tip de date diferit. Acesta este echivalent cu exemplul de jos, doar că în cel de jos avem, într-o măsură evitabilă prin folosirea templateurilor, cod duplicat.

Exemplu

#include <iostream>

int Sum(int a, int b) {
    return a + b;
}

float Sum(float a, float b) {
    return a + b;
}

int main() {
    std::cout << Sum(4, 5) << '\n';
    std::cout << Sum(4.5f, 5.6f);

    return 0;
}

Templateurile merg la fel de bine și pe clase. De asemenea,prin templateuri putem să trimitem și valori. Un caz ar putea fi dimensiunile unui array, deoarece acestea trebuie specificate la run time. Acestor variabile le pot fi atribuite și valori default.

Exemplu

#include <iostream>

template <typename T, int N = 10>
class vector {
public:
    T v[N];

    T& operator [] (int idx) {
        return v[idx];
    }
};

int main() {
    vector <char, 5> v;
    v[1] = 'a';
    std::cout << v[1];

    vector <int> u;

    return 0;
}

Când se folosesc

Templateurile sunt foarte puternice, deoarece acestea comunică direct cu compilatorul și îi spun practic ce să scrie. Acestea sunt scalabile, un exemplu pentru asta fiind o funcție de logging, care poate afișa inturi, stringuri, floaturi, etc, doar folosind un typename. Cu toate acestea, templateurile nu treebuiesc abuzate. Codul poate deveni mult mai greu de urmărit, iar erorile pot fi mult mai greu de depistat. Unele companii chiar le interzic.

Task

  1. Implementați o funcție de sortare care să poate sorta mai multe tipuri de date pe baza unei variabile m_ID. Definiți și cel puțin 2 tipuri de date cu un membru m_ID, care să aibă un getter și un setter pentru această variabilă privată.

  2. Faceți un singleton Logger, care să conțină o singură metodă Log, care poate afișa orice tip standard de date, și o variabilă cu getter și setter, care să numere de câte ori ați afișat ceva.

  3. Adăugați templateuri la vectorul vostru.

  4. Adăugați templateuri si la iterator.