- int max 42
- double max 3.4
- string max ilea
Corectari: Morpheus
Notă: Tutorialul acesta este mai vechi și l-am mai postat și pe alte forumuri. Menționez lucrul acesta ca să se evite orice fel de suspiciuni.
I Functii template
Acesta este un tutorial despre functii template urmand sa fac cat de curant unul despre clase template.
1.1 Sintaxa and other
Functile template reprezinta o familie de functii.
Ele arata ca si o functie normala cu exceptia unor elemente care le vom explica. Urmatoarea functie template returneaza maximul dintre doua valori:
- template <typename T>
- const T& maxim (const T& a, const T& b)
- {
- //Daca a<b returneaza b, altfel returneaza a
- return a < b ? b : a;
- }
Aceasta definitie produce o familie de functii care returneaza maximul a doua valori care sunt pasate prin intermediul parametrilor a si b. Tipul acestor parametrii este lasat liber cu un parametru template T.
Dupa cum am vazut in exemplu, parametrii template trebuie sa fie anuntati cu o syntaxa de genul :
- template < lista_de_parametrii_separata_prin_virgule >
In exemplul nostru lista de parametrii continea doar un parametru si anume typename T. Observam de asemenea semnul mai mic si mai mare, acestea au rolul de paranteze unghiulare. Observam si cuvantul cheie typename care este folosit pentru a defini tipul parametrilor. Din motive istorice se poate folosit si class in loc de typename. Cuvantul cheie typename a fost introdus relativ recent in evolutia limbajului c++.
Se prefera folosirea cuvantului typename deoarece cuvantul class poate induce in eroare iar intre ele nu exista practic nici o diferenta.
Urmatorul cod prezinta folosirea acestei functii template:
- #include <iostream>
- #include <string>
- using namespace std;
- //functia template
- template <typename T>
- const T& maxim (const T& a, const T& b)
- {
- return a < b ? b : a;
- }
- int main()
- {
- int i = 42;
- int j = 34;
- cout << "int max "<<maxim(i,j)<<endl;
- double d1 = 3.4;
- double d2 = -6.7;
- cout << "double max " << maxim(d1,d2) <<endl;
- string s1 = "ilea";
- string s2 = "cristian";
- cout << "string max " << maxim(s1,s2) <<endl;
- return 0;
- }
In acest program, functia maxim() este apelata de 3 ori. Odata pentru doua int-uri apoi pentru doua double-uri si apoi pentru doua string-uri. De fiecare data maximul este determinat iar programul afiseaza urmatoarele:
Functiile template nu sunt compilate in entitati singulare care sa poata primi orice tip ca parametru. In schimb se genereaza entitati diferite din template pentru fiecare tip pentru care functia template este folosita. Sa luam ca exemplu prima apelare a lui maxim().
- int i = 42;
- int j = 34;
- cout << "int max "<< maxim(i,j) << endl;
Acesti prim apel foloseste functia template cu int ca si parametru in loc la T. Codul echivalent pentru aceasta functie este:
- const int& maxim (const int& a, const int& b)
- {
- return a < b ? b : a;
- }
Procesul de a inlocui parametrii template cu tipuri concrete se numeste instantiere. Similar cu apelul lui maxim() pentru int asa se instantiaza si pentru double sau string:
- const double& maxim (const double& d1, const double& d2); {/* ... */}
- const string& maxim (const string& s1, const string& s1); {/* ... */}
1.2 Deducerea parametrilor
Cand se apeleaza o functie template ca si maxim() , pentru unele cazuri, parametrii template sunt determinati in functie de argumentele care le pasam. Daca pasam doua int-uri parametrului T , compilatorul C++ trebuie sa concluzioneze ca T trebuie sa fie int. De retinut faptul ca nu este permisa conversia de tip. T trebuie sa se potriveasca exact.
De exemplu:
- template <typename T>
- const T& maxim (const T& a, const T& b);
- /* ..cod.. */
- maxim(4,7) // OK: T este int pentru amandoua argumentele
- maxim(4,4.2) // Eroare: primul T este int apoi al doilea este double
Putem rezolva aceasta eroare in 2 moduri:
1.Putem folosi operatorul static_cast <tip_de_conversie> (variabila_de_convertit)
- maxim(static_cast<double>(4),4.2)
Astfel 4 este convertit in double ( 4.0 ) iar T este double pentru amandoua argumentele.
2.Specificam explicit tipul lui T
- maxim<double>(4,4.2)
Functile template au doua tipuri de parametrii:
1.Parametrii template care sunt declarati intre paranteze unghiulare inaintea numelui functiei template.
- template <typename T> //T este parametru template
2.Parametrii de apelare care sunt declarati in paranteze rotunde dupa numele functiei template
- const T& maxim (const T& a, const T& b) //a si b sunt parametrii de apelare
Puteti sa aveti oricat de multi parametrii template vreti.
Putem defini maxim() cu doi parametrii de apelare de doua tipuri diferite:
- template <typename T1, typename T2>
- T1 maxim (const T1& a, const T2& b)
- {
- return a < b ? b : a;
- }
- /* ...cod... */
- maxim(4,4.2) // e ok, dar tipul primului argument defineste tipul care va fi returnat
Aceasta pare o metoda buna, dar are acest exemplu are si parti proaste. Problema este ca tipul de returnare trebuie sa fie declarat. Daca folositi unul din parametrii ca si tip de returnare, argumentul celuilalt parametru va fi convertit in in tipul primului , in ciuda intentilor utilizatorului. C++ nu pune la dispozitie un mod de a specifica "cel mai puternic tip" (desi poti sa faci asa ceva cu ajutorul unor tehnici smechere de template programming pe care le voi prezenta in alt tutorial). Deci depinde de argumentul cu care se face apelarea. Maximul dintre 42 si 66.66 poate sa fie double 66.66 sau int 66.
Putem sa introducem un al treilea argument template pentru a defini tipul de care il va returna functia template:
- template <typename T1, typename T2, typename RT>
- RT maxim (const T1& a, const T2& b);
Totusi RT nu poate fi dedus. Ca o consecinta, noi trebuie sa specificam lista de argumente in mod explicit
- template <typename T1, typename T2, typename RT>
- RT maxim (const T1& a, const T2& b);
- maxim<int,double,double>(4,4.2) //ok, dar cam aiurea
Putem sa rearanjam parametrii template pentru ca restul sa fie deduse in mod automat:
- template <typename RT, typename T1, typename T2> //observati asezarea
- RT maxim (const T1& a, const T2& b);
- /* ... code ... */
- maxim<double>(4,4.2) // ok, tipul pe care il returneaza maxim() este double
In acest exemplu , apelul maxim<double> seteaza RT ca fiind explicit double, dar parametri T1 si T2 sunt dedusi ca fiind int si double.
1.3 Supraincarcarea functiilor template
Ca si functile normale , functile template pot fi supraincarcate. Adica poti avea diferite definitii de functii cu acelasi nume si atunci cand un numele este folosit in apelarea functiei , compilatorul C++ trebuie sa decida care din candidati (definitii) sa aleaga. Regulile folosite pentru a decide ce definitie sa fie folosita pot deveni oarecum complicate chiar si fara template-uri. Mai jos vom discuta despre supraincarcarea functilor template.
- //maximul a doua valori int
- const int& maxim (const int& a, const int& b)
- {
- return a < b ? b : a;
- }
- //maximul dintre doua valori de orice tip
- template <typename T>
- const T& maxim (const T& a, const T& b)
- {
- return a < b ? b : a;
- }
- //maximul dintre 3 valori de orice tip
- template <typename T>
- const T& maxim (const T& a, const T& b, const T& c)
- {
- return maxim (maxim(a,b), c);
- }
- int main()
- {
- maxim(7, 42, 68); //1 apeleaza functia template pentru 3 argumente
- maxim(7.0, 42.0); //2 apeleaza maxim<double> (prin deductie de argument)
- maxim('a', 'b'); //3 apeleaza maxim<char> (prin deductie de argument)
- maxim(7, 42); //4 apeleaza functia nontemplate pentru doua valori int
- maxim<>(7, 42); //5 apeleaza maxim<int> (prin deductie de argument)
- maxim<double>(7, 42); //6 apeleaza maxim<double> (fara deductie de argument , noi i-am impus ce sa foloseasca)
- maxim('a', 42.7); //7 apeleaza functia nontemplate pentru doua valori int
- return 0;
- }
Dupa cum vedem in exemplul de mai sus , o functie nontemplate poate sa coexiste cu functii template care au acelasi nume. Procesul de supraincarcare prefera functia nontemplate. Al patrulea apel cade sub aceasta regula.
- maxim(7, 42); // amandoua valorile se incadreaza perfect in functia nontemplate
Daca templateul poate genera o functie care sa se potriveasca mai bine, compilatorul alege functia template. Acest lucru este demonstrat de al 2-lea si al 3-lea apel:
- maxim(7.0, 42.0); // apeleaza maxim<double> (prin deductie de argument)
- maxim('a', 'b'); // apeleaza maxim<char> (prin deductie de argument)
Este posibil sa pasam o lista fara argumente template. Aceasta sintaxa spune compilatorului ca doar o functie template poate sa rezolve acel apel, dar toti toti parametrii template trebuie dedusi din tipul argumentelor cu care apelam functia.
- maxim<>(7, 42) //doar functia nontemplate poate primi argumente de tip diferit
Deoarece conversia de tip nu este posibila pentru functile template, ultimul apel foloseste functia nontemplate (iar valorile 'a' si 42,7 sunt convertite in int)



