Wynik tego programu:
#include <iostream>
class c1
{
public:
c1& meth1(int* ar) {
std::cout << "method 1" << std::endl;
*ar = 1;
return *this;
}
void meth2(int ar)
{
std::cout << "method 2:"<< ar << std::endl;
}
};
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu).meth2(nu);
}
Jest:
method 1
method 2:0
Dlaczego nu
nie ma 1, kiedy meth2()
zaczyna się?
c++
chaining
operator-precedence
Moises Viñas
źródło
źródło
nu
,&nu
a następniec
na stos w tej kolejności, następnie wywołaćmeth1
, odłożyć wynik na stos, a następnie wywołaćmeth2
, podczas gdy konwencja wywoływania oparta na rejestrze chciałaby załadujc
i&nu
do rejestrów, wywołajmeth1
, załadujnu
do rejestru, a następnie wywołajmeth2
.Odpowiedzi:
Ponieważ kolejność oceny jest nieokreślona.
Widzisz
nu
wmain
ocenianej na0
zanim nawetmeth1
nazywa. To jest problem z łańcuchem. Radzę tego nie robić.Po prostu utwórz ładny, prosty, przejrzysty, czytelny i łatwy do zrozumienia program:
źródło
<<
Dla danych wyjściowych i "konstruktorów obiektów" dla złożonych obiektów ze zbyt dużą liczbą argumentów dla konstruktorów - ale bardzo źle się miesza z argumentami wyjściowymi).meth1
imeth2
jest zdefiniowana, ale ocena parametru dlameth2
może nastąpić przedmeth1
wywołaniem ...?meth2(meth1(c, &nu), nu)
Myślę, że ta część projektu normy dotycząca kolejności ocen jest istotna:
i również:
Więc dla twojej linii
c.meth1(&nu).meth2(nu);
zastanów się, co dzieje się w operatorze pod względem operatora wywołania funkcji dla końcowego wywołaniameth2
, więc wyraźnie widzimy podział na wyrażenie i argument z przyrostkanu
:Wartości wyrażenia przyrostka i argumentu dla końcowego wywołania funkcji (tj. Wyrażenia przyrostka
c.meth1(&nu).meth2
inu
) nie są sekwencjonowane względem siebie, zgodnie z powyższą regułą wywołania funkcji . Dlatego efekt uboczny obliczania wyrażenia postfiksowego na obiekcie skalarnymar
jest bez kolejności w stosunku do oceny argumentunu
przedmeth2
wywołaniem funkcji. Zgodnie z powyższą regułą wykonywania programu jest to niezdefiniowane zachowanie.Innymi słowy, kompilator nie musi oceniać
nu
argumentumeth2
wywołania pometh1
wywołaniu - może zakładać, że nie ma skutków ubocznychmeth1
wpływania nanu
ocenę.Kod asemblera utworzony przez powyższe zawiera następującą sekwencję w
main
funkcji:nu
jest przydzielana na stosie i inicjalizowana z wartością 0.ebx
w moim przypadku) otrzymuje kopię wartościnu
nu
ic
są ładowane do rejestrów parametrówmeth1
jest nazywanynu
webx
rejestrze są ładowane do rejestrów parametrówmeth2
jest nazywanyCo najważniejsze, w kroku 5 powyżej kompilator umożliwia
nu
ponowne użycie zbuforowanej wartości z kroku 2 w wywołaniu funkcjimeth2
. W tym przypadku pomija możliwość, któranu
mogła zostać zmieniona przez wezwanie dometh1
- „niezdefiniowanego zachowania” w akcji.UWAGA: Ta odpowiedź zmieniła się zasadniczo w stosunku do pierwotnej formy. Moje początkowe wyjaśnienie w zakresie skutków ubocznych obliczania argumentów, które nie były sekwencjonowane przed ostatecznym wywołaniem funkcji, było niepoprawne, ponieważ tak jest. Problem polega na tym, że obliczenia samych operandów są nieokreślone.
źródło
meth1
jest wykonywany wcześniejmeth2
, ale parametr formeth2
jest wartościąnu
buforowaną w rejestrze przed wywołaniemmeth1
- tj. Kompilator zignorował potencjalne skutki uboczne, którymi są zgodne z moją odpowiedzią.c.meth1(&nu).meth2
) i ocena argumentu tego wywołania (nu
) są generalnie bez kolejności, ale 1) ich skutki uboczne są sekwencjonowane przed wejściemmeth2
i 2) ponieważc.meth1(&nu)
jest wywołaniem funkcji , jest nieokreślony sekwencjonowany z ocenąnu
. Wewnątrzmeth2
, gdyby w jakiś sposób uzyskał wskaźnik do zmiennej wmain
, zawsze zobaczyłby 1.meth2
, jak wspomniano w punkcie 3 strony cppreference, którą cytujesz (którą również pominąłeś we właściwy sposób zacytować).W standardzie C ++ z 1998 r., Sekcja 5, pkt 4
(Pominąłem odniesienie do przypisu # 53, który nie dotyczy tego pytania).
Zasadniczo
&nu
należy ocenić przed wywołaniemc1::meth1()
inu
przed wywołaniemc1::meth2()
. Nie ma jednak żadnego wymagania, którenu
należy ocenić wcześniej&nu
(np. Dozwolone jest, abynu
najpierw zostać ocenione&nu
, a następniec1::meth1()
wywoływane - co może być tym, co robi twój kompilator). Wyrażenie*ar = 1
wc1::meth1()
związku z tym nie jest gwarantowana być oceniane przednu
wmain()
oceniana jest, aby być przekazywanec1::meth2()
.Późniejsze standardy C ++ (których obecnie nie mam na komputerze, którego używam dziś wieczorem) mają zasadniczo tę samą klauzulę.
źródło
Myślę, że podczas kompilacji, zanim naprawdę zostaną wywołane funkcje met1 i met2, parametry zostały do nich przekazane. Mam na myśli, kiedy używasz „c.meth1 (& nu) .meth2 (nu);” wartość nu = 0 została przekazana do meth2, więc nie ma znaczenia, czy "nu" zostanie zmienione później.
możesz spróbować tego:
otrzyma odpowiedź, której chcesz
źródło