Zwracany typ „?:” (Trójskładnikowy operator warunkowy)

208

Dlaczego pierwszy zwraca referencję?

int x = 1;
int y = 2;
(x > y ? x : y) = 100;

Podczas gdy drugi nie?

int x = 1;
long y = 2;
(x > y ? x : y) = 100;

W rzeczywistości druga nie kompilowała się wcale - „nie pozostała wartość przypisania”.

Yola
źródło
1
hmm, to jest jak znalezienie specjalnego przypadku do pieczenia chleba, nie przyszło do tego raz
Ulterior
Ponieważ przypisanie typu do wyrażenia oznaczałoby rzutowanie co najmniej jednego terminu, termin ten nie byłby więcej wartością l.
Yves Daoust,

Odpowiedzi:

173

Wyrażenia nie mają typów zwracanych, mają typ i - jak wiadomo w najnowszym standardzie C ++ - kategorię wartości.

Warunkowe ekspresja może być lwartość lub RValue . To jest jego kategoria wartości. (Jest to nieco uproszczenie, C++11ponieważ mamy wartości lvalu, xvalues ​​i prvalues.)

W bardzo szerokim i prostym znaczeniu, wartość odnosi się do obiektu w pamięci i wartości jest po prostu wartością, która niekoniecznie musi być dołączona do obiektu w pamięci.

Wyrażenie przypisania przypisuje wartość do obiektu, więc sprawa jest przypisany do musi być lwartość .

Dla ekspresji warunkowej ( ?:) być lwartość (ponownie, w ogólnym i prosty), drugie i trzecie operandy musi lwartościami tego samego typu . Wynika to z faktu, że typ i wartość kategorii wyrażenia warunkowego jest określana w czasie kompilacji i musi być odpowiednia, czy warunek jest spełniony. Jeżeli jeden z argumentów musi być przekształcany do innego rodzaju, aby dopasować inne niż ekspresja warunkowego nie może być lwartość na skutek tej przemiany nie byłby lwartość .

Odniesienia do ISO / IEC 14882: 2011:

3.10 [basic.lval] Wartości i wartości lv (o kategoriach wartości)

5.15 [expr.cond] Operator warunkowy (reguły dla jakiego typu i kategorii wartości ma wyrażenie warunkowe)

5.17 [expr.ass] Operatory przypisania i przypisania złożonego (wymaganie, aby wartości przypisania musiały być wartością modyfikowalną)

CB Bailey
źródło
3
A kiedy czytałem o Xvalue i prvalue (ponieważ nie słyszałem o nich przed twoją odpowiedzią), natknąłem się na ten przydatny post SO: stackoverflow.com/questions/3601602/...
puszysty
an rvalue is just a value that may not necessarily be *attached* to an object in memory.Czy możesz to wyjaśnić w prostszy sposób? . Co masz na myśli type and value *category*? Dzięki
Mr.Anubis
@SoulReaper: prvalue, xvalue, glvaluesą kategoriami wartości.
Xeo
@ Xeo Doceniam pomoc, ale czy możesz powiedzieć, co rozumie przez wartość, jest tylko wartością, która niekoniecznie musi być dołączona do obiektu w pamięci. ? z przykładem?
Mr.Anubis
@SoulReaper: Myślę, że on mówi o takich rzeczach true, this, enumwartości. Te rzeczy są wartościami („czystymi” wartościami), ale nie żyją w pamięci.
Xeo
57

Typ ?:wyrażenia trójskładnikowego jest typowym typem jego drugiego i trzeciego argumentu. Jeśli oba typy są takie same, otrzymasz referencję z powrotem. Jeśli można je ze sobą konwertować, jeden zostaje wybrany, a drugi przekształcony (w tym przypadku promowany). Ponieważ nie można zwrócić odwołania do wartości tymczasowej (zmiennej konwertowanej / promowanej), jego typ jest typem wartości.

Xeo
źródło
ale y jest większe niż x, więc nie ma potrzeby promocji w tym konkretnym przypadku, może zwrócić odwołanie do y. Hmm ... Ale zgadzam się, to byłoby dziwne.
Yola,
1
@ Mr.TAMER: Wolę przekopać się przez standard. : <
Xeo
3
@Yola: Ponieważ typy są pojęciem czasu kompilacji w C ++, rzeczywista wartość zwracana wyrażenia nie ma znaczenia.
Xeo
1
Nie dostajesz referencji, dostajesz wartość.
Suma,
1
@Xeo: Jednak nie w terminologii C ++;)
Sebastian Mach
19

Nie może wrócić do lwartość ponieważ będzie musiał niejawnie promować rodzaj xdopasować rodzaj y(ponieważ obie strony :nie są tego samego typu), oraz, że musi stworzyć tymczasowy.


Co mówi standard? ( n1905 )

Wyrażenia 5.17 Operatory przypisania i przypisania złożonego

5,17 / 3

Jeśli drugi i trzeci operand mają różne typy i albo ma typ klasy (prawdopodobnie kwalifikowany do cv), podejmowana jest próba konwersji każdego z tych operandów na typ drugiego. Proces określania, czy wyrażenie operandu E1 typu T1 można przekonwertować, aby pasowało do wyrażenia operandu E2 typu T2, definiuje się w następujący sposób:

- Jeśli E2 jest wartością: E1 można przekonwertować, aby dopasować E2, jeśli E1 można niejawnie przekonwertować (klauzula 4) na typ „odwołanie do T2”, z zastrzeżeniem ograniczenia, że ​​w konwersji odniesienie musi być bezpośrednio powiązane (8.5.3 ) do E1.

- Jeśli E2 jest wartością lub jeżeli nie można wykonać powyższej konwersji:

- jeśli E1 i E2 mają typ klasy, a podstawowe typy klas są takie same lub jeden z nich jest klasą podstawową drugiego: E1 można przekonwertować na dopasowanie do E2, jeśli klasa T2 jest tego samego typu lub klasa podstawowa , klasa T1 i kwalifikacja cv dla T2 to ta sama kwalifikacja cv co lub wyższa kwalifikacja cv niż kwalifikacja cv dla T1. Jeśli konwersja zostanie zastosowana, E1 zostanie zmienione na wartość typu T2, która nadal odnosi się do oryginalnego obiektu klasy źródłowej (lub jego odpowiedniego podobiektu). [ Uwaga: to znaczy, że nie wykonuje się żadnej kopii. - uwaga końcowa ] poprzez zainicjowanie kopii tymczasowej typu T2 z E1 i użycie tej tymczasowej jako przekonwertowanego argumentu.

W przeciwnym razie (tj. Jeśli E1 lub E2 ma typ nieklasowy, lub jeśli oba mają typy klas, ale klasy bazowe nie są ani takie same, ani jedna z klas podstawowych drugiej): E1 można przekonwertować na dopasowanie E2, jeśli E1 może być niejawnie przekonwertowany na typ, jaki miałoby wyrażenie E2, gdyby E2 został przekonwertowany na wartość (lub typ, który ma, jeśli E2 jest wartością).

Za pomocą tego procesu określa się, czy drugi argument można przekonwertować, aby pasował do trzeciego argumentu, i czy trzeci argument można przekonwertować, aby pasował do drugiego argumentu. Jeśli oba można przekonwertować lub jeden można przekonwertować, ale konwersja jest niejednoznaczna, program jest źle sformułowany. Jeśli nie można przekonwertować żadnego z nich, operandy pozostają niezmienione, a dalsze sprawdzanie odbywa się w sposób opisany poniżej. Jeśli możliwa jest dokładnie jedna konwersja, konwersja ta jest stosowana do wybranego operandu, a konwertowany operand jest używany zamiast oryginalnego operandu do końca tej sekcji.


5,17 / 4

Jeśli drugi i trzeci operand są wartościami lv i mają ten sam typ, wynik jest tego typu i jest wartością i jest polem bitowym, jeśli drugi lub trzeci operand jest polem bitowym lub jeśli oba są bitami pola.


5,17 / 5

W przeciwnym razie wynikiem jest wartość. Jeśli drugi i trzeci operand nie mają tego samego typu i albo ma typ klasy (prawdopodobnie kwalifikowany do cv), rozdzielczość przeciążenia jest używana do określenia konwersji (jeśli występują) do operandów (13.3.1.2, 13.6) . Jeśli rozdzielczość przeciążenia nie powiedzie się, program jest źle sformułowany. W przeciwnym razie ustalone w ten sposób konwersje zostaną zastosowane, a skonwertowane operandy zostaną użyte zamiast oryginalnych operandów w pozostałej części tej sekcji.

Filip Roséen - refp
źródło