Inne zachowanie operatora przecinka w C ++ z powrotem?

83

To (zwróć uwagę na operator przecinka ):

#include <iostream>
int main() {
    int x;
    x = 2, 3;
    std::cout << x << "\n";
    return 0;
}

wyjścia 2 .

Jeśli jednak używasz returnz operatorem przecinka, to:

#include <iostream>
int f() { return 2, 3; }
int main() {
    int x;
    x = f();
    std::cout << x << "\n";
    return 0;
}

wyjścia 3 .

Dlaczego operator przecinka zachowuje się inaczej z return?

xyz
źródło

Odpowiedzi:

140

Zgodnie z pierwszeństwem operatora , operator przecinka ma niższy priorytet niż operator=, więc x = 2,3;jest równoważny (x = 2),3;. (Pierwszeństwo operatorów określa, w jaki sposób operator będzie powiązany ze swoimi argumentami, ściślej lub luźniej niż inne operatory, zgodnie z ich priorytetami.)

Zwróć uwagę, że wyrażenie z przecinkiem jest (x = 2),3tutaj, a nie 2,3. x = 2jest najpierw oceniany (a jego skutki uboczne są zakończone), następnie wynik jest odrzucany, a następnie 3oceniany (w rzeczywistości nic nie robi). Dlatego wartość xjest 2. Zauważ, że 3jest to wynik całego wyrażenia z przecinkiem (tj. x = 2,3), Nie będzie ono używane do przypisywania x. (Zmień na x = (2,3);, xzostanie przypisany 3.)

Ponieważ return 2,3;wyrażenie z przecinkiem jest 2,3, 2jest oceniane, a następnie jego wynik jest odrzucany, a następnie 3oceniany i zwracany jako wynik całego wyrażenia z przecinkiem, które jest później zwracane przez instrukcję return .


Dodatkowe informacje o wyrażeniach i instrukcjach

Wyrażenie to sekwencja operatorów i ich operandów, która określa obliczenia.

x = 2,3;jest wyrażeniem , x = 2,3jest wyrażeniem tutaj.

Wyrażenie, po którym następuje średnik, jest instrukcją.

Składnia: attr(optional) expression(optional) ; (1)

return 2,3;jest instrukcją skoku ( instrukcją powrotu ), 2,3jest wyrażeniem tutaj.

Składnia: attr(optional) return expression(optional) ; (1)

songyuanyao
źródło
1
dobre wytłumaczenie. Ale czy są jakieś praktyczne zastosowania? czy tylko błędy do zrobienia?
Jean-François Fabre
7
@ Jean-FrançoisFabre IMO to po prostu mylące, w ogóle nieprzydatne.
songyuanyao,
11
Widziałem to raz lub dwa razy używane w forpętlach, kiedy, co dziwne, może uczynić kod wyraźniejszym w obliczeniach numerycznych.
Batszeba,
6
@ Jean-FrançoisFabre: jak mówi Bathesheba, jest tak, że można napisać coś i += 1, j += 2w rodzaju pętli for. Ktoś zdecydował, że gramatyka C ++ (a raczej gramatyka C, ponieważ ta część została skopiowana stamtąd) jest już wystarczająco skomplikowana bez próby zdefiniowania, że ​​pierwszeństwo przecinka jest wyższe niż przypisanie podczas pisania, x = 2, 3ale niższe podczas pisania x = 2, y = 3!
Steve Jessop,
1
@ Holger: średnik kończy instrukcję, nie jest operatorem. To jest coś, co można zmienić, aby była bardziej przejrzysta. „x = 2, 3” jest wyrażeniem z 2 operatorami i ze względu na obsługę (;;), = ma wyższy priorytet. (Jak wszyscy mówili). Ale „return 2, 3”; to stwierdzenie zawierające wyrażenie „2, 3”. Z technicznego punktu widzenia słowo kluczowe „powrót” nie ma pierwszeństwa. (Chociaż skutecznie , ponieważ jest to część instrukcji, która akceptuje wyrażenie, jest analizowana jako ostatnia - niższy "priorytet" niż jakikolwiek operator w wyrażeniu).
Micha Berger
32

Operator przecinka (znany również jako separacja wyrażenia ) jest oceniany od lewej do prawej. Więc return 2,3;jest równoważne return 3;.

Ocena x = 2,3;jest (x = 2), 3;ze względu na pierwszeństwo operatora . Ocena jest nadal od lewej do prawej, a całe wyrażenie ma wartość 3, czego skutkiem ubocznym jest xprzyjęcie wartości 2.

Batszeba
źródło
2
Czy możesz edytować i bardziej szczegółowo opisać operator separacji wyrażeń ? Jak wspomniałem w komentarzu do odpowiedzi @ songyuanyao, rozumiem dlaczego return 2,3i return (2,3)są takie same. Uważałem, że to pierwsze powinno być (return 2),3.
xyz,
@BiagioFesta dobrze to wyjaśnia.
Batszeba,
1
@ prakharsingh95 return 2to instrukcja (jak np. utworzone przez for,while,if), a nie wyrażenie. Nie możesz pisać np . f(return 2)Lub 2+return 2. Więc (return 2),3jest niepoprawna składniowo.
chi
@chi Tak, masz rację. Miałem na myśli, że spodziewałem return 2, 3się, że będę interpretowany jako (return 2), 3.
xyz,
2
@ prakharsingh95 zgodnie z gramatyką C ++, returnmoże wystąpić tylko w następujących przypadkach: (a) return expression_opt ; i (b) return braced-init-list ; .
MM
20

To oświadczenie:

  x = 2,3;

składa się z dwóch wyrażeń :

> x = 2
> 3

Ponieważ pierwszeństwo operatorów , =ma wyższy priorytet niż przecinek ,, więc x = 2jest oceniany i po 3 . Wtedy xbędzie równy 2.


W returnzamian:

int f(){ return 2,3; }

Składnia języka to:

return <expression>

Uwaga return nie jest częścią wyrażenia.

Więc w tym przypadku dwa wyrażenia zostaną ocenione:

> 2
> 3

Ale tylko druga ( 3) zostanie zwrócona.

Biagio Festa
źródło
2
UV'd. Bardzo wybredny, ale byłoby miło, gdybyś oznaczył <expression>jako wyraźnie opcjonalny (z punktu widzenia gramatyki).
Batszeba,
2
W drzewie analizy pliku znajduje się 5 wyrażeń x=2,3. Oba literały 2i 3znajdują się na dole drzewa analizy, podobnie jak identyfikator x. Są to wyrażenia ważne indywidualnie. Środki operator pierwszeństwa że =występuje niższy w drzewie parsowania, i łączy w sobie dwa wyrażenia xi 2do czwartego wyrazu x=2. Wreszcie, piąte wyrażenie jest tworzone przez operator przecinka łączący jego dwie strony x=2i 3. Jednak niepoprawnie określasz, że pierwszeństwo operatorów określa kolejność oceny. Tak nie jest. Kolejność oceny jest określana przez zasady sekwencjonowania.
MSalters
2
Głosowałem za wspomnieniem, że powrót nie jest częścią wyrażenia
Daniel Jour,
@MSalters Zgadzam się z tobą, ale po prostu niepoprawnie użyłem słowa „ ponieważ ” zamiast „ od ”. Coś, co mój angielski nie jest tak doskonały! ; - =
Biagio Festa
2
Czy „wyrażenie makro” jest tutaj terminem technicznym? Używanie go, gdy istnieją również „wyrażenia makro” w znaczeniu preprocesora, wydaje się nieco zagmatwane.
senshin
2

Spróbuj zastosować uproszczone podejście, podkreślając pierwszeństwo nawiasami:

( x = 2 ), 3;

return ( 2, 3 );

Teraz widzimy operator binarny "," działający w ten sam sposób na obu, od lewej do prawej.

Luciano
źródło
1
Najtrudniejsze jest uświadomienie sobie, że x = 2, 3samo jest wyrażeniem, podczas returngdy jest return <expression>. Więc czytasz je jako (x = 2, 3)i (2, 3).
xyz