Odniesienie (referencja) może nie tylko być parametrem, ale i wartością zwracaną przez funkcję. Następująca deklaracja
int& fun(int);oznacza, że funkcja zwraca pewną zmienną typu int przez referencję, czyli na przykład wyrażenie fun(3) jest inną nazwą pewnej istniejącej zmiennej, która została podana w funkcji w instrukcji return.
To, że wyrażenie z wywołaniem funkcji jest traktowane jako nazwa
istniejącej zmiennej, a więc jako l-wartość, oznacza, że
wywołanie funkcji zwracającej referencję może pojawić się po
lewej stronie przypisania, co wygląda trochę szokująco i nie
stosuje się zbyt często; tym niemniej nie ma tu błędu.
Przykładem może być funkcja
funmax
z poniższego programu:
1. #include <iostream> 2. #include <cmath> 3. using namespace std; 4. 5. double potegi(double&,double*); 6. int* kwadrat(int*); 7. int& funmax(int[],int); 8. 9. int main() { 10. 11. // argumenty wskaźnikowe i referencyjne 12. double u = 4, v; 13. double szesc = potegi(u, &v); ➊ 14. cout << "Szescian: " << szesc << "; kwadrat: " 15. << u << "; pierwiastek: " << v << endl; 16. 17. // to też ma sens 18. int i = 4; 19. cout << "20? : " << ++*kwadrat(&i)+3 << endl; ➋ 20. 21. // funkcja zwracająca referencję 22. int tab[] = {1,4,6,2}; 23. cout << "Tablica przed: "; 24. for ( i = 0; i < 4; i++ ) cout << tab[i] << " "; 25. cout << endl; 26. 27. funmax(tab,4) = 0; ➌ 28. 29. cout << "Tablica po : "; 30. for ( i = 0; i < 4; i++ ) cout << tab[i] << " "; 31. cout << endl; 32. } 33. 34. double potegi(double& u, double* v) { ➍ 35. double x = u; 36. u *= u; 37. *v = sqrt(x); 38. return u*x; 39. } 40. 41. int* kwadrat(int* p) { ➎ 42. *p *= *p; 43. return p; 44. } 45. 46. int& funmax(int* tab, int ile) { ➏ 47. int i, ind = 0; 48. for ( i = 1; i < ile; i++ ) 49. if ( tab[i] > tab[ind] ) ind = i; 50. return tab[ind]; 51. }
Funkcja potegi ma jeden argument referencyjny i jeden wskaźnikowy. Przez referencję, jako pierwszy argument, przesyłamy liczbę dodatnią: funkcja oblicza jej kwadrat, sześcian i pierwiatek kwadratowy (➍). Kwadrat argumentu jest w funkcji przypisywany do tej samej zmiennej u, która była pierwszym argumentem tej funkcji. Ponieważ argument jest referencyjny, w funkcji main po wywołaniu funkcji potegi wartość ta będzie nową wartością zmiennej u — stara wartość, wynosząca 4, zostanie zamazana.
Jako drugi argument wysyłamy do funkcji kopię adresu zmiennej v (➊). W funkcji do zmiennej o tym adresie wpisywana jest wartość pierwiastka kwadratowego pierwszego argumentu. Tak więc po powrocie z funkcji zmienna v w programie głównym będzie miała wartość 2. Funkcja potegi zwraca, przez wartość, sześcian argumentu.
Po wywołaniu funkcji potegi sześcian pierwotnej wartości zmiennej u mamy w zmiennej szesc, kwadrat jest nową wartością zmiennej u, a pierwiastek jest wartością zmiennej v: potwierdza to wydruk z programu
Szescian: 64; kwadrat: 16; pierwiastek: 2 20? : 20 Tablica przed: 1 4 6 2 Tablica po : 1 4 0 2Dość karkołomna konstrukcja użyta jest w linii ➋. Funkcja kwadrat (➎) oblicza kwadrat argumentu przekazanego przez wskaźnik (do funkcji przekazujemy kopię adresu zmiennej i). Obliczona wartość jest wpisywana do zmiennej wskazywanej przez wskaźnik, a więc do zmiennej i z programu głównego. Jednocześnie wskaźnik do tej samej zmiennej jest zwracany w instrukcji return — zauważmy, że nie jest to wskaźnik do zmiennej lokalnej, ale do istniejącej w programie głównym zmiennej i. Po wywołaniu funkcji wartością wyrażenia kwadrat(&i) jest zatem wskaźnik do zmiennej i, której wartość uległa zmianie i wynosi teraz 16 (= 4×4). Za pomocą operatora gwiazdki dokonujemy dereferencji, a więc wyrażenie *kwadrat(&i) jest równoważne zmiennej i, o wartości 16. Tę wartość zwiększamy o jeden za pomocą operatora zwiększenia i do wyniku dodajemy 3; zatem wartością całego wyrażenia ++*kwadrat(&i)+3 jest 20. Wbrew pozorom takie zawiłe konstrukcje dość często zdarzają się w realnych programach (choć może nie powinny).
Przyjrzyjmy się teraz funkcji funmax (➏). Do funkcji w tradycyjny sposób wysyłamy tablicę i jej wymiar (➌). Funkcja szuka największego elementu i zwraca tenże element przez referencję, czego z linii zawierającej instrukcję return nie widać, ale widać z nagłówka w deklaracji/definicji. Zatem wyrażenie funmax(tab,4) jest nazwą tej zmiennej, która jest największym elementem tablicy, czyli w naszym przykładzie tab[2]. Wyrażenie to stoi po lewej stronie przypisania, zatem zmienna ta jest zerowana, o czym przekonuje nas wydruk.
T.R. Werner, 21 lutego 2016; 20:17