In C++ putem avea mai multe functii cu acelasi nume dar trebuie ca parametrii ori sa fie de tipuri diferite, ori sa difere numarul de parametrii, ori parametrii sa fie in alta ordine (ma refer la tipul lor de ex.
int, int, float
si float, int, int
nu la denumire, asta nu conteaza). De exemplu:
void f(int x) {} // numar diferit de parametrii fata de functiile de mai jos
void f(int x, float y) {} // difera ordinea tipurilor parametriilor fata de functia de mai jos
void f(float x, int y) {}
Totusi ai grija la parametrii cu valori implicite, de exemplu mai jos avem eroare pentru ca apelul de functie este ambiguu, nu se stie care functie sa se aleaga.
#include <iostream>
using namespace std;
void f(int x) {}
void f(int x, int y = 4) {}
int main() {
f(5); // eroare aici
}
Tehnic vorbind functiile trebuie sa aiba signaturi diferite. Signatura se refera la numele functiei, numarul, ordinea si tipul parametrilor.
Daca avem ceva de genul:
#include <iostream>
using namespace std;
void f(int x, int y) {}
void f(double x, int y) {}
int main() {
f(2, 2.3);
}
Cum se alege ce functie se apeleaza? Compilatorul se uita pe rand la fiecare argument si creaza o multime de functii candidat pentru fiecare argument, adica tipul argumentului (cand apelezi functia) se poate converti (cumva) in tipul parametrului (in definitia functiei). Pentru fiecare argument se creeaza o multime de functii care se potrivesc cel mai bine (chiar daca exista multe functii canditat unele fac mai multa "treaba" decat altele sa converteasca, de aceea se aleg cele care se potrivesc cel mai bine). Din multimile astea de functii care se potrivesc cel mai bine pentru fiecare argument se face o intersectie si functia rezultata este cea care se apeleaza. Daca intersectia e vida sau contine mai mult de 1 functie avem eroare de compilare.
De exemplu:
#include <iostream>
using namespace std;
class MyClass {
// o clasa goala pentru exemplu
};
void f(MyClass x, long long y) {} // functia 1
void f(long long x, MyClass y) {} // functia 2
void f(MyClass x, MyClass y) {} // functia 3
int main() {
int x = 4;
MyClass y;
f(x, y);
f(x, x);
}
Sa ne uitam la linia 16:
f(x, y);
. Mai intai la primul argument de tip MyClass
, multimea functiilor candidat = {functia 1, functia 2},multimea functiilor care ce potrivesc cel mai bine = {functia 1, functia 2}. Acum sa ne uitam la al doilea argument de tip int
, multimea functiilor candidat = multimea functiilor care se potrivesc cel mai bine = {functia 1} (pentru ca se poate converti int
in long long
).
{functia 1, functia 2} ∩ {functia 1} = {functia 1}. Asta inseamna ca in final pentru
f(x, y);
se apeleaza functia 1.
Daca ne uitam la linia 17:
f(x, x);
, pentru primul argument multimea functiilor care se potrivesc cel mai bine = {functia 2}, pentru al doilea argument multimea functiilor care se potrivesc cel mai bine = {functia 1}, iar {functia 2} ∩ {functia 1} = Ø si deci eroare.
In general daca avem o functie care e supraincarcata si avem doua variante F1 si F2, cum alege compilatorul functia care se potriveste cel mai bine? Functia F1 este mai "buna" decat F2 daca in primul rand conversiile implicite pe care le face F1 (de la tipul argumentelor la tipul parametrilor) nu sunt mai rele decat cele facute de F2 (pt. fiecare argument) si in plus:
- E cel putin un argument in F1 a carui conversie implicita este mai buna decat conversia implicita pentru acel argument in F2
- Sau daca nu e asa, dar doar in cazul conversiilor definite de tine (gen operatorul de cast intr-o clasa), daca conversia implicita de la tipul returnat de F1 la tipul destinatie este mai buna decat conversia implicita de la tipul returnat de F2 la tipul destinatie.
class MyClass { public: operator float() { return 5.5; } operator int() { return 1; } }; int main() { MyClass a; double x = a; // se apeleza prima functie vezi mai jos de ce cout << x; // se afiseaza 5.5 }
- Sau daca nu e asa, F1 este non-template iar F2 este template sau ambele sunt template dar F1 este mai specializata (dar despre template vorbesc separat).
Acum ce inseamna ca o conversie e mai buna decat alta? Exista 3 tipuri (in ordinea importantei):
-
Exact match
Nu trebuie sa se faca nicio "conversie" sau daca se face se considera exact match.void f(int& x); void f(double x); int x = 42; f(x); // argument type is int; exact match with int& ///////////////////////////////////////// void g(int* p); void g(void* p); int a[100]; g(a); // calls f(int*); exact match with array-to-pointer conversion -
Promotion
Aici intra integral promotion si floating-point promotion. Promotia e un tip special de conversie pentru tipurile de date built-in (int,floatetc.) si este garantat ca nu schimba valoarea. (adica tipul de date la care se converteste poate reprezenta exact orice valoare a tipului de date de la care se converteste)Integral promotion- signed charsausigned shortse pot converti laint
- unsigned charsauunsigned shortse pot converti laintdacaintpoate retine toate valorile sau launsigned intdaca nu poate. (e posibil ca short/char si int sa aiba acelasi nr. de biti pe un calculator si de aceea nu poti din unsigned short/char in int)
- charse poate converti laintsauunsigned int(depinde daca princharte referi lasigned charsauunsigned char)
- boolse poate converti laint,falsedevine0iartruedevine1
Floating-point promotion- Se refera doar la conversia de la floatladouble.
-
Conversion
Aici intra integral conversion, floating-point conversion, floating-integral conversion, bool conversionIntegral conversion- aici intra conversii dintre 2 tipuri de date ce reprezinta nr. intregi dar nu e promotion, de exemplu din intinlongsau dinunsigned shortinshort
Floating-point conversion- aici intra conversii dintre 2 tipuri de date ce reprezinta nr. cu virgula dar nu e promotion, de exemplu din doubleinfloatsau dinfloatinlong doublevoid f(double); void f(long double); f(0.0f);Se apeleaza prima functie pentru ca promotia (de lafloatladouble) este mai buna decat conversia (de lafloatlalong double)
Floating-integral conversion- aici intra conversii dintre un tip de date ce reprezinta nr. cu virgula si un tip de date care reprezinta nr. intregi de exemplu din doubleinintsau dinintinfloatvoid f(int); void f(long double); f(0.0f);Eroare de compilare, apelul este ambiguu, avem 2 conversii (de lafloatlaintsi de lafloatlalong double)
Bool conversion- de exemplu din intinboolsau dinfloatinbool. (de ex.bool x = 5; // true)
- aici intra conversii dintre 2 tipuri de date ce reprezinta nr. intregi dar nu e promotion, de exemplu din