Podczas czytania tego wyjaśnienia na temat lvalues i rvalues, wyskoczyły mi następujące wiersze kodu:
int& foo();
foo() = 42; // OK, foo() is an lvalue
Wypróbowałem to w g ++, ale kompilator mówi „niezdefiniowane odwołanie do foo ()”. Jeśli dodam
int foo()
{
return 2;
}
int main()
{
int& foo();
foo() = 42;
}
Kompiluje się dobrze, ale uruchomienie go powoduje błąd segmentacji . Tylko linia
int& foo();
sama się kompiluje i działa bez żadnych problemów.
Co oznacza ten kod? Jak przypisać wartość do wywołania funkcji i dlaczego nie jest to wartość r?
c++
function
return-by-reference
Sossisos
źródło
źródło
Odpowiedzi:
Wyjaśnienie zakłada, że istnieje jakaś rozsądna implementacja, dla
foo
której zwraca referencję l-wartości do poprawnejint
.Taka implementacja może być:
Teraz, ponieważ
foo
zwraca odniesienie do lwartości, możemy przypisać coś do zwracanej wartości, na przykład:To zaktualizuje globalną
a
wartością42
, którą możemy sprawdzić, uzyskując dostęp do zmiennej bezpośrednio lub wywołującfoo
ponownie:źródło
Wszystkie inne odpowiedzi deklarują wartość statyczną wewnątrz funkcji. Myślę, że to może cię zmylić, więc spójrz na to:
Ponieważ
highest()
zwraca odwołanie, możesz przypisać mu wartość. Po uruchomieniub
zostanie zmieniony na 11. Jeśli zmienisz inicjalizację naa
, powiedzmy, 8, toa
zostanie zmieniony na 11. To jest kod, który może faktycznie służyć celowi, w przeciwieństwie do innych przykładów.źródło
Deklaruje funkcję o nazwie foo, która zwraca odwołanie do pliku
int
. To, czego te przykłady nie dają, to definicja tej funkcji, którą można skompilować. Jeśli używamyTeraz mamy funkcję, która zwraca odniesienie do
bar
. ponieważ barstatic
będzie żył po wywołaniu funkcji, więc zwrócenie do niej odwołania jest bezpieczne. Teraz, jeśli to zrobimyTo, co się dzieje, to przypisanie 42 do,
bar
ponieważ przypisujemy odwołanie, a odniesienie jest tylko aliasem dlabar
. Jeśli ponownie wywołamy tę funkcję jakWypisze 42, ponieważ ustawiliśmy
bar
to powyżej.źródło
int &foo();
deklaruje funkcję wywoływanąfoo()
z typem zwracanymint&
. Jeśli wywołasz tę funkcję bez podania treści, prawdopodobnie wystąpi niezdefiniowany błąd odwołania.W drugiej próbie zapewniłeś funkcję
int foo()
. To ma inny typ powrotu niż funkcja zadeklarowana przezint& foo();
. Masz więc dwie takie same deklaracjefoo
, które nie są zgodne, co narusza regułę jednej definicji, powodując niezdefiniowane zachowanie (nie jest wymagana diagnostyka).Aby coś działało, usuń deklarację funkcji lokalnej. Jak widzieliście, mogą prowadzić do cichego, niezdefiniowanego zachowania. Zamiast tego używaj tylko deklaracji funkcji poza jakąkolwiek funkcją. Twój program mógłby wyglądać następująco:
źródło
int& foo();
jest funkcją zwracającą odwołanie doint
. Podana funkcja zwraca sięint
bez odniesienia.Możesz to zrobić
źródło
int & foo();
oznacza, żefoo()
zwraca odwołanie do zmiennej.Rozważ ten kod:
Ten kod drukuje:
$ ./a.out k = 5
Ponieważ
foo()
zwraca odniesienie do zmiennej globalnejk
.W poprawionym kodzie rzutujesz zwróconą wartość na odwołanie, które jest wtedy nieprawidłowe.
źródło
W tym kontekście & oznacza odniesienie - więc foo zwraca odniesienie do int, a nie do int.
Nie jestem pewien, czy pracowałeś jeszcze ze wskaźnikami, ale to podobny pomysł, tak naprawdę nie zwracasz wartości z funkcji - zamiast tego przekazujesz informacje potrzebne do znalezienia lokalizacji w pamięci, w której to int jest.
Podsumowując, nie przypisujesz wartości do wywołania funkcji - używasz funkcji do uzyskania odwołania, a następnie przypisujesz wartość, do której się odwołujemy, do nowej wartości. Łatwo jest pomyśleć, że wszystko dzieje się na raz, ale w rzeczywistości komputer robi wszystko w określonej kolejności.
Jeśli zastanawiasz się - powodem, dla którego otrzymujesz segfault, jest to, że zwracasz literał numeryczny `` 2 '' - więc jest to dokładny błąd, który otrzymasz, gdybyś zdefiniował stałą int, a następnie spróbował zmodyfikować jej wartość.
Jeśli jeszcze nie nauczyłeś się wskaźników i pamięci dynamicznej, to najpierw polecam, ponieważ jest kilka pojęć, które moim zdaniem są trudne do zrozumienia, chyba że nauczysz się ich wszystkich naraz.
źródło
Przykładowy kod na połączonej stronie jest tylko fikcyjną deklaracją funkcji. Nie kompiluje się, ale gdybyś miał zdefiniowaną jakąś funkcję, działałaby ogólnie. Przykład oznaczał „Gdybyś miał funkcję z tym podpisem, mógłbyś jej użyć w ten sposób”.
W twoim przykładzie
foo
wyraźnie zwraca lwartość na podstawie podpisu, ale zwracasz wartość r, która jest konwertowana na lwartość. To wyraźnie jest zdeterminowane, aby zawieść. Mógłbyś:i odniesie sukces, zmieniając wartość x, mówiąc:
źródło
Funkcja, którą masz, foo (), jest funkcją, która zwraca referencję do liczby całkowitej.
Powiedzmy, że pierwotnie foo zwróciło 5, a później, w swojej funkcji głównej, mówisz
foo() = 10;
, że następnie wypisuje foo, wypisze 10 zamiast 5.Mam nadzieję, że to ma sens :)
Jestem też nowy w programowaniu. Ciekawie jest widzieć takie pytania, które sprawiają, że myślisz! :)
źródło