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:
1
void f(int x) {} // numar diferit de parametrii fata de functiile de mai jos2
void f(int x, float y) {} // difera ordinea tipurilor parametriilor fata de functia de mai jos3
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.
1
#include <iostream>2
using namespace std;3
4
void f(int x) {}5
void f(int x, int y = 4) {}6
7
int main() {8
f(5); // eroare aici9
}
Tehnic vorbind functiile trebuie sa aiba signaturi diferite. Signatura se refera la numele functiei, numarul, ordinea si tipul parametrilor.
Daca avem ceva de genul:
1
#include <iostream>2
using namespace std;3
4
void f(int x, int y) {}5
void f(double x, int y) {}6
7
int main() {8
f(2, 2.3);9
}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:
1
#include <iostream>2
using namespace std;3
4
class MyClass {5
// o clasa goala pentru exemplu6
};7
8
void f(MyClass x, long long y) {} // functia 19
void f(long long x, MyClass y) {} // functia 210
void f(MyClass x, MyClass y) {} // functia 311
12
int main() {13
int x = 4;14
MyClass y;15
16
f(x, y);17
f(x, x);18
}
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.
1
class MyClass {2public:3operator float() { return 5.5; }4operator int() { return 1; }5};67int main() {8MyClass a;9double x = a; // se apeleza prima functie vezi mai jos de ce10cout << x; // se afiseaza 5.511} - 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.1void f(int& x);2void f(double x);3int x = 42;4f(x); // argument type is int; exact match with int&56/////////////////////////////////////////78void g(int* p);9void g(void* p);1011int a[100];12g(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 (,intetc.) 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)floatIntegral promotionsausigned charse pot converti lasigned shortintsauunsigned charse pot converti launsigned shortdacaintpoate retine toate valorile sau laintdaca 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)unsigned intse poate converti lacharsauint(depinde daca prinunsigned intte referi lacharsausigned char)unsigned charse poate converti labool,intdevinefalse
iar0devinetrue1
Floating-point promotion- Se refera doar la conversia de la
lafloat.double
-
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
inintsau dinlonginunsigned shortshort
Floating-point conversion- aici intra conversii dintre 2 tipuri de date ce reprezinta nr. cu virgula dar nu e promotion, de exemplu din
indoublesau dinfloatinfloatlong doubleSe apeleaza prima functie pentru ca promotia (de la1void f(double);2void f(long double);3f(0.0f);lafloat) este mai buna decat conversia (de ladoublelafloat)long 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
indoublesau dinintinintfloatEroare de compilare, apelul este ambiguu, avem 2 conversii (de la1void f(int);2void f(long double);3f(0.0f);lafloatsi de laintlafloat)long double
Bool conversion- de exemplu din
inintsau dinboolinfloat. (de ex.bool)bool x = 5; // true
- aici intra conversii dintre 2 tipuri de date ce reprezinta nr. intregi dar nu e promotion, de exemplu din