Jak działa operator przecinka w C ++?
Na przykład, jeśli to zrobię:
a = b, c;
Czy koniec równa się b lub c?
(Tak, wiem, że jest to łatwe do przetestowania - wystarczy udokumentować tutaj, aby ktoś mógł szybko znaleźć odpowiedź).
Aktualizacja: to pytanie ujawniło niuans podczas korzystania z operatora przecinka. Żeby to udokumentować:
a = b, c; // a is set to the value of b!
a = (b, c); // a is set to the value of c!
To pytanie zostało zainspirowane literówką w kodzie. Co miało być
a = b;
c = d;
Zmienił się w
a = b, // <- Note comma typo!
c = d;
c++
comma-operator
Joe Schneider
źródło
źródło
a = (b, c);
.a = b, c = d;
faktycznie działa tak, jak zamierzonoa = b; c = d;
?b
id
są wartościami funkcji, które używają (i modyfikują) wspólny stan, kolejność wykonywania nie jest zdefiniowana doC++17
.Odpowiedzi:
Byłoby równe
b
.Operator przecinka ma niższy priorytet niż przypisanie.
źródło
Zwróć uwagę, że operator przecinka może być przeciążony w C ++. Faktyczne zachowanie może zatem bardzo różnić się od oczekiwanego.
Na przykład Boost.Spirit używa operatora przecinka całkiem sprytnie do zaimplementowania inicjatorów list dla tabel symboli. Dzięki temu następująca składnia jest możliwa i znacząca:
Zauważ, że ze względu na pierwszeństwo operatorów kod jest (celowo!) Identyczny z
Oznacza to, że pierwszy wywołany operator
keywords.operator =("and")
zwraca obiekt proxy, na którymoperator,
wywoływane są pozostałe s:źródło
char[]
, którego nie można przeciążać. Kod celowo najpierw wywołuje,operator=
a następnieoperator,
dla każdego pozostałego elementu.Operator przecinka ma najniższy priorytet ze wszystkich operatorów C / C ++. Dlatego zawsze jest to ostatnie, które wiąże się z wyrażeniem, co oznacza:
jest równa:
Innym interesującym faktem jest to, że operator przecinka wprowadza punkt sekwencji . Oznacza to, że wyrażenie:
gwarantuje ze swoich trzech podwyrażeń ( a + b , c () i d ) oceniano w kolejności. Jest to istotne, jeśli mają skutki uboczne. Zwykle kompilatory mogą oceniać podwyrażenia w dowolnej kolejności; na przykład w wywołaniu funkcji:
argumenty można oceniać w dowolnej kolejności. Zauważ, że przecinki w wywołaniu funkcji nie są operatorami; są separatorami.
źródło
,
ma tak niski priorytet, że nawet pozostaje w tyle za sobą ;) ... To znaczy: przecinek-jako- operator ma niższy priorytet niż przecinek-jako- separator . Tak więc, jeśli chcesz użyć operatora przecinka jako operatora w pojedynczym argumencie funkcji, przypisaniu zmiennej lub innej liście oddzielonej przecinkami - musisz użyć nawiasów, np .:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
Operator przecinka:
Domyślna wersja operatora przecinka jest zdefiniowana dla wszystkich typów (wbudowanych i niestandardowych) i działa w następujący sposób - biorąc pod uwagę
exprA , exprB
:exprA
jest ocenianyexprA
jest ignorowanyexprB
jest ocenianyexprB
jest zwracany jako wynik całego wyrażeniaW przypadku większości operatorów kompilator może wybrać kolejność wykonywania, a nawet wymagane jest, aby pominąć wykonanie, jeśli nie wpływa to na końcowy wynik (np.
false && foo()
Pominie wywołaniefoo
). Nie dotyczy to jednak operatora przecinka i powyższe kroki zawsze będą miały miejsce * .W praktyce domyślny operator przecinka działa prawie tak samo jak średnik. Różnica polega na tym, że dwa wyrażenia oddzielone średnikiem tworzą dwie oddzielne instrukcje, podczas gdy separacja przecinkami zachowuje wszystko jako jedno wyrażenie. Z tego powodu operator przecinka jest czasami używany w następujących scenariuszach:
if( HERE )
for
pętlifor ( HERE ; ; )
if (foo) HERE ;
(nie rób tego, to naprawdę brzydkie!)Jeśli instrukcja nie jest wyrażeniem, średnika nie można zastąpić przecinkiem. Na przykład te są niedozwolone:
(foo, if (foo) bar)
(if
nie jest wyrażeniem)W Twoim przypadku mamy:
a=b, c;
, odpowiednika=b; c;
, zakładając, żea
jest to typ, który nie przeciąża operatora przecinka.a = b, c = d;
odpowiednika=b; c=d;
, zakładając, żea
jest to typ, który nie przeciąża operatora przecinka.Zwróć uwagę, że nie każdy przecinek jest w rzeczywistości operatorem przecinka. Kilka przecinków, które mają zupełnie inne znaczenie:
int a, b;
--- lista deklaracji zmiennych jest oddzielona przecinkami, ale nie są to operatory przecinkoweint a=5, b=3;
--- jest to również lista deklaracji zmiennych oddzielonych przecinkamifoo(x,y)
--- lista argumentów oddzielonych przecinkami. W rzeczywistościx
iy
można je oceniać w dowolnej kolejności!FOO(x,y)
--- rozdzielona przecinkami lista argumentów makrfoo<a,b>
--- oddzielona przecinkami lista argumentów szablonówint foo(int a, int b)
--- lista parametrów oddzielonych przecinkamiFoo::Foo() : a(5), b(3) {}
--- oddzielona przecinkami lista inicjalizacyjna w konstruktorze klasy* Nie jest to do końca prawdą, jeśli zastosujesz optymalizacje. Jeśli kompilator rozpozna, że określony fragment kodu nie ma absolutnie żadnego wpływu na resztę, usunie niepotrzebne instrukcje.
Więcej informacji: http://en.wikipedia.org/wiki/Comma_operator
źródło
operator ,
jest przeciążony, tracisz jakiekolwiek gwarancje asocjatywności (tak jak tracisz właściwości zwarcioweoperator&&
ioperator||
jeśli są przeciążone)?a, b, c
zawsze znaczy(a, b), c
i nigdya, (b, c)
. Ta ostatnia interpretacja może nawet prowadzić do błędu kompilacji, jeśli elementy są różnych typów. To, czego możesz chcieć, to kolejność oceny argumentów? Nie jestem tego pewien, ale być może masz rację: może się zdarzyć, żec
zostanie to ocenione wcześniej,(a, b)
nawet jeśli przecinek jest lewostronny.struct Foo { Foo() : a(5), b(3) {} int b; int a; }
Ewaluujeb(3)
wcześnieja(5)
. Jest to ważne, jeśli lista jest tak:Foo() : a(5), b(a) {}
. b nie będzie ustawione na 5, ale raczej niezainicjowaną wartość a, o której kompilator może ostrzegać lub nie.Wartość
a
będzieb
, ale wartość wyrażenia będziec
. To jest wa byłby równy
b
id
byłby równyc
.źródło
a = b; d = c;
?Wartość b zostanie przypisana do a. Nic się nie stanie c
źródło
Wartość a będzie równa b, ponieważ operator przecinka ma niższy priorytet niż operator przypisania.
źródło
Tak Operator przecinka ma niski priorytet niż operator przypisania
Wynik: i = 3
Ponieważ operator przecinka zawsze zwraca wartość najbardziej po prawej stronie.
W przypadku operatora przecinka z operatorem przypisania:
Ouput: i = 1
Jak wiemy, operator przecinka ma niższy priorytet niż przypisanie .....
źródło
i = 1;
w tym wierszu?Po pierwsze: przecinek w rzeczywistości nie jest operatorem, dla kompilatora jest po prostu tokenem, który ma znaczenie w kontekście innych tokenów.
Co to oznacza i po co się przejmować?
Przykład 1:
Aby zrozumieć różnicę między znaczeniem tego samego tokena w innym kontekście, spójrzmy na ten przykład:
Zwykle C ++ Początkujący pomyśli, że to wyrażenie nie mogli / byłoby porównać rzeczy, ale to jest absolutnie źle, rozumieniu
<
,>
a,
żetony depent od kontekstu użycia.Prawidłowa interpretacja powyższego przykładu jest oczywiście taka, że jest to wstawienie szablonu.
Przykład 2:
Kiedy piszemy typową pętlę for z więcej niż jedną zmienną inicjalizacyjną i / lub więcej niż jednym wyrażeniem, które należy wykonać po każdej iteracji pętli, również używamy przecinka:
Znaczenie przecinka zależy od kontekstu użycia, tutaj jest to kontekst
for
konstrukcji.Co właściwie oznacza przecinek w kontekście?
Aby to jeszcze bardziej skomplikować (jak zawsze w C ++), sam operator przecinka może zostać przeciążony (dzięki Konradowi Rudolphowi za wskazanie tego).
Wracając do pytania, Kodeks
oznacza coś w rodzaju kompilatora
ponieważ pierwszeństwo z
=
tokena / podmiotu jest wyższy niż priorytet z,
tokena.i jest to interpretowane w kontekście
(zwróć uwagę, że interpretacja zależy od kontekstu, tutaj nie jest to ani wywołanie funkcji / metody, ani instancja szablonu).
źródło