Kolejność obliczeń w parametrach funkcji C ++

90

Jeśli mamy trzy funkcje (foo, bar i baz), które są tak złożone ...

foo(bar(), baz())

Czy standard C ++ gwarantuje, że pasek zostanie oceniony przed baz?

Clark Gaebel
źródło

Odpowiedzi:

102

Nie, nie ma takiej gwarancji. Nie jest określony zgodnie ze standardem C ++.

Bjarne Stroustrup również mówi to wyraźnie w "Języku programowania C ++", 3. wydanie, sekcja 6.2.2, z pewnym uzasadnieniem:

Lepszy kod można wygenerować bez ograniczeń dotyczących kolejności oceny wyrażeń

Chociaż technicznie odnosi się to do wcześniejszej części tej samej sekcji, która mówi, że kolejność oceny części wyrażenia jest również nieokreślona, ​​tj.

int x = f(2) + g(3);   // unspecified whether f() or g() is called first
Eli Bendersky
źródło
Potrafię zaakceptować tę odpowiedź w 8 minut ... Myślę, że zostaję trochę w pobliżu!
Clark Gaebel
4
Tak, ale lepszy kod można by ZAPISAĆ (= czystszy), jeśli kolejność oceny wyrażenia była ŚREDNIA, co jest generalnie dużo ważniejsze niż generowanie kodu. Zobacz ten przykład: stackoverflow.com/questions/43612592/… No więc, Stroustrup.
Bill Kotsias
1
Jeśli zamawianie ma znaczenie, możesz samodzielnie wykonać sekwencjonowanie. Zrobienie inaczej zawsze wiązałoby się z kosztami czegoś, co nie zawsze (rzadko?) Ma znaczenie. Myślę, że jedyną rzeczą, z którą zgadza się większość programistów C ++, jest zasada nie płacenia za to, czego nie używasz.
tweej
3
Czy nie powinno to być „nieokreślone zachowanie” zamiast „nieokreślone”?
GoodDeeds
1
@ChrisDodd odrzucenie zaakceptowanej odpowiedzi z powodu użycia słowa „nieokreślony” w porównaniu z „nieokreślonym” wydaje mi się złośliwą pedanterią… Nie powiedziałem, że to „nieokreślone zachowanie”, a poza tym wydaje mi się, że jest to „niezdefiniowane” i „nieokreślone” równoznaczny? W każdym razie zaproponowanie zmiany w odpowiedzi byłoby bardziej produktywnym sposobem omówienia tego
Eli Bendersky,
21

Z [5.2.2] wywołania funkcji,

Kolejność oceny argumentów jest nieokreślona. Wszystkie skutki uboczne obliczeń wyrażeń argumentów obowiązują przed wprowadzeniem funkcji.

Dlatego nie ma gwarancji, że bar()będzie działać wcześniej baz(), tylko to bar()i baz()zostanie wywołane wcześniej foo.

Zwróć również uwagę na [5] Wyrażenia, że:

z wyjątkiem przypadków, w których zaznaczono [np. specjalne reguły dla &&i ||], kolejność obliczania operandów poszczególnych operatorów i podwyrażeń poszczególnych wyrażeń oraz kolejność, w jakiej występują skutki uboczne, nie jest określona.

więc nawet jeśli zostały pytaniem, czy bar()potrwa zanim baz()się foo(bar() + baz())kolejność jest nadal nieokreślone.

Daniel Trebbien
źródło
4
Przykład "uwagi specjalnej" z [5.14] Operator logiczny AND: "W przeciwieństwie do tego &, &&gwarantuje ocenę od lewej do prawej: drugi argument nie jest oceniany, jeśli pierwszy operand jest false."
Daniel Trebbien
20

Nie ma określonej kolejności dla bar () i baz () - jedyną rzeczą, o której mówi Standard, jest to, że oba zostaną ocenione przed wywołaniem foo (). Ze standardu C ++, sekcja 5.2.2 / 8:

Kolejność oceny argumentów jest nieokreślona.


źródło
4
Fakt, że są one oceniane przed foo (), jest przynajmniej trochę uspokajający.
Bill Kotsias
1
@BillKotsias Standard mówi również, że wywołania funkcji nie mogą się nakładać (tj. Implementacja nie może uruchomić linii 1 bar, następnie linii 1 baz, następnie linii 2 baritd.), Co również jest miłe. :-)
melpomene
3

W C ++ 11 odpowiedni tekst można znaleźć w 8.3.6 Default arguments / 9 (moje podkreślenie)

Argumenty domyślne są oceniane przy każdym wywołaniu funkcji. Kolejność obliczania argumentów funkcji jest nieokreślona . W konsekwencji parametry funkcji nie powinny być używane w argumencie domyślnym, nawet jeśli nie są oceniane.

Ta sama słówka jest używana również w standardzie C ++ 14 i znajduje się w tej samej sekcji .

R Sahu
źródło
0

Jak inni już zauważyli, standard nie podaje żadnych wskazówek co do kolejności oceny dla tego konkretnego scenariusza. Ta kolejność oceny jest następnie pozostawiana kompilatorowi, a kompilator może mieć gwarancję.

Należy pamiętać, że standard C ++ jest tak naprawdę językiem, który instruuje kompilator, jak konstruować asembler / kod maszynowy. Norma to tylko jedna część równania. Tam, gdzie standard jest niejednoznaczny lub ma konkretnie zdefiniowaną implementację, należy zwrócić się do kompilatora i zrozumieć, w jaki sposób tłumaczy on instrukcje C ++ na prawdziwy język maszynowy.

Tak więc, jeśli kolejność oceny jest wymaganiem lub przynajmniej ważna, a zgodność z różnymi kompilatorami nie jest wymaganiem, zbadaj, w jaki sposób Twój kompilator ostatecznie połączy to w całość, Twoja odpowiedź może ostatecznie leżeć w tym miejscu. Zauważ, że kompilator może zmienić swoją metodologię w przyszłości

Programista Erudycji
źródło