8. Funkcje

8.4. Przeciążanie nazw funkcji

Wspomniałem,  że w C++ nazwa funkcji nie jest jej unikalnym identyfikatorem – że kompilator automatycznie dodaje do nazw przyrostek zawierający typy wszystkich argumentów w odpowiedniej kolejności. Ten mechanizm jest wykorzystany do przeciążania nazw funkcji, czyli pozwolenia na to, by w programie mogło występować kilka funkcji o tej samej nazwie. 

Przeciążanie nazwy funkcji (overloading) - występowanie w programie pod tą samą nazwą wielu funkcji różniących się listą argumentów

Przy czym kompilator musi mieć możliwość odgadnięcia, o którą funkcję chodzi autorowi, więc - muszą się różnić typami  parametrów na liście (nie samymi nazwami - bo one nie są wykorzystywane przy wywołaniu), i nie można wykorzystać do przeciążania typu wartości zwracanej (wartość zwracają można "porzucić" - i nie byłoby przesłanki do wyboru odpowiedniej wersji funkcji. 

Przykłady prawidłowego i nieprawidłowego przeciążania:


// prawidłowo
int funkcja(int _a);
int funkcja(int _a, int _y);
int funkcja(double _a);
int funkcja(string _a);

// nieprawidłowo
int funkcja(int _a);
double funkcja(int _a);

Kompilator sam dopasuje odpowiednią funkcję w momencie wywołania, kierując się następującymi zasadami:

  • Ścisła zgodność (bez konwersji lub konwersje trywialne – tablica na wskaźnik, stała na zmienną, nazwa funkcji na funkcję)
  • Zgodność z zastosowaniem promowania typów całościowych (bool na int, char na int i odpowiedniki bez znaku)
  • Zgodność z zachowaniem standardowych konwersji (całkowite na zmiennoprzecinkowe i vice versa, rzutowania klas, rzutowanie na void*)
  • Zgodność z zachowaniem definiowanych konwersji (dla klas)
  • Zgodność z wielokropkiem w deklaracji funkcji.
Przykład:


void drukuj(int _x);
void drukuj(const char* _x);
void drukuj(double _x);
void drukuj(long _x);
void drukuj(char _x);

void h(char _c, int i, short s, float f)
{
  // ścisla zdodnosc
  drukuj(c);
  drukuj(i);
  // trywialna konwersja
  drukuj('c');
  drukuj(49);
  drukuj(0);
  drukuj("A");

  // promocja w zakresie typów całościowych
  drukuj(s); // wywoła drukuj(int)
  // promocja w zakresie konw. standardowych
  drukuj(f); // wywoła drukuj(double)
}

Jeśli znaleziono więcej niż jedną pasującą funkcję na tym samym poziomie – będzie błąd kompilacji. Jak się taki błąd pojawi – co robić? Najlepiej ręcznie rozwiać wątpliwości kompilatora:


void f1(char);
void f1(long);

void f2(char*);
void f2(int*);
...
  int i;
  f1(i); // niejednoznaczne
  f1(long(i)); // ok
  f2(0); // niejednoznaczne
  f2(static_cast<int*>(0)); // ok

double pow(int x, int y);
double pow(double x, double y);
...
  pow(2.0, 2); // niejednoznaczne
  pow(2.0, (double)2); // i już ok.
</int*>