Rückgabetyp '?:' (Ternärer bedingter Operator)


208

Warum gibt der erste eine Referenz zurück?

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

Während der zweite nicht?

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

Tatsächlich hat der zweite überhaupt nicht kompiliert - "kein Wert übrig von der Zuordnung".


1
hmm, das ist wie ein Sonderfall für das Backen von Brot, kam nicht einmal dazu
Ulterior


Da das Zuweisen eines Typs zum Ausdruck eine Umwandlung von mindestens einem Term implizieren würde, wäre dieser Term kein l-Wert mehr.
Yves Daoust

Antworten:


173

Ausdrücke haben keine Rückgabetypen, sondern einen Typ und - wie im neuesten C ++ - Standard bekannt - eine Wertekategorie.

Ein bedingter Ausdruck kann ein l-Wert oder ein r-Wert sein . Dies ist seine Wertekategorie. (Dies ist eine Vereinfachung, da C++11wir l-Werte, x-Werte und pr-Werte haben.)

In sehr weiten und einfachen Worten bezieht sich ein l-Wert auf ein Objekt im Speicher und ein R - Wert ist nur ein Wert, der auf ein Objekt im Speicher nicht unbedingt angebracht werden kann.

Ein Zuweisungsausdruck weist auf ein Objekt einen Wert so das Ding ein zugewiesen werden muss lvalue .

Damit ein bedingter Ausdruck ( ?:) ein l-Wert ist (wiederum in weiten und einfachen Worten), müssen der zweite und der dritte Operand l-Werte desselben Typs sein . Dies liegt daran, dass der Typ und die Wertekategorie eines bedingten Ausdrucks zur Kompilierungszeit festgelegt werden und angemessen sein müssen, ob die Bedingung erfüllt ist oder nicht. Wenn einer der Operanden in einen anderen Typ konvertiert werden muss, um mit dem anderen übereinzustimmen, kann der bedingte Ausdruck kein l-Wert sein, da das Ergebnis dieser Konvertierung kein l-Wert wäre .

ISO / IEC 14882: 2011 Referenzen:

3.10 [basic.lval] L-Werte und r-Werte (über Wertkategorien)

5.15 [Ausdruck] Bedingter Operator (Regeln für den Typ und die Wertkategorie eines bedingten Ausdrucks)

5.17 [expr.ass] Zuweisungs- und zusammengesetzte Zuweisungsoperatoren (Anforderung, dass die lhs einer Zuweisung ein modifizierbarer l-Wert sein müssen)


3
Und als ich über xvalue und prvalue las (da ich vor Ihrer Antwort noch nichts davon gehört hatte), stieß ich auf diesen praktischen SO-Beitrag: stackoverflow.com/questions/3601602/…
flauschiger

an rvalue is just a value that may not necessarily be *attached* to an object in memory.Können Sie das einfacher erklären? . Auch was meinst du damit type and value *category*? Vielen Dank
Mr.Anubis

@SoulReaper: prvalue, xvalue, glvaluesind Wertkategorien.
Xeo

@Xeo Ich schätze Hilfe, aber können Sie sagen, was er unter einem Wert versteht, ist nur ein Wert, der nicht unbedingt an ein Objekt im Speicher angehängt werden muss. ? mit Beispiel?
Mr. Anubis

@SoulReaper: Ich er spricht über die Dinge denken mag true, this, enumWerte. Diese Dinge sind Werte ("reine" Werte), leben aber nicht in Erinnerung.
Xeo

57

Der Typ des ternären ?:Ausdrucks ist der übliche Typ seines zweiten und dritten Arguments. Wenn beide Typen gleich sind, erhalten Sie eine Referenz zurück. Wenn sie ineinander konvertierbar sind, wird einer ausgewählt und der andere konvertiert (in diesem Fall befördert). Da Sie keine lvalue-Referenz auf eine temporäre Variable (die konvertierte / heraufgestufte Variable) zurückgeben können, ist ihr Typ ein Werttyp.


aber y größer als x, so dass für diesen speziellen Fall keine Werbung erforderlich ist, kann ein Verweis auf y zurückgegeben werden. Hmm ... Aber ich stimme zu, es wäre seltsam.
Yola

1
@ Mr.TAMER: Ich würde lieber durch den Standard graben. : <
Xeo

3
@Yola: Da Typen ein Kompilierung Konzept in C ++, die tatsächliche Rückkehr Wert des Ausdrucks spielt keine Rolle.
Xeo

1
Sie erhalten keine Referenz zurück, Sie erhalten einen Wert.
Suma

1
@Xeo: Nicht in C ++ Terminologie;)
Sebastian Mach

19

Es kann keine Rückkehr lvalue , da sie implizit die Art zu fördern haben wird von xder Art des entsprechen y(da beide Seiten :nicht vom gleichen Typ sind), und mit , dass es eine temporäre zu erstellen.


Was sagt der Standard? ( n1905 )

Ausdrücke 5.17 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren

5.17 / 3

Wenn der zweite und der dritte Operand unterschiedliche Typen haben und entweder einen (möglicherweise cv-qualifizierten) Klassentyp haben, wird versucht, jeden dieser Operanden in den Typ des anderen zu konvertieren. Der Prozess zum Bestimmen, ob ein Operandenausdruck E1 vom Typ T1 konvertiert werden kann, um mit einem Operandenausdruck E2 vom Typ T2 übereinzustimmen, ist wie folgt definiert:

- Wenn E2 ein l-Wert ist: E1 kann konvertiert werden, um mit E2 übereinzustimmen, wenn E1 implizit in den Typ „Referenz auf T2“ konvertiert werden kann (Abschnitt 4), unter der Bedingung, dass die Referenz bei der Konvertierung direkt gebunden werden muss (8.5.3 ) bis E1.

- Wenn E2 ein r-Wert ist oder wenn die obige Konvertierung nicht möglich ist:

- Wenn E1 und E2 einen Klassentyp haben und die zugrunde liegenden Klassentypen gleich sind oder einer eine Basisklasse des anderen ist: E1 kann in E2 konvertiert werden, wenn die Klasse von T2 der gleiche Typ wie oder eine Basisklasse von ist , die Klasse von T1 und die Lebenslaufqualifikation von T2 ist dieselbe Lebenslaufqualifikation wie oder eine höhere Lebenslaufqualifikation als die Lebenslaufqualifikation von T1. Wenn die Konvertierung angewendet wird, wird E1 in einen Wert vom Typ T2 geändert, der sich immer noch auf das ursprüngliche Quellklassenobjekt (oder das entsprechende Unterobjekt davon) bezieht. [ Hinweis: Das heißt, es wird keine Kopie erstellt. - Endnote ] durch Kopieren und Initialisieren eines Temporärs vom Typ T2 aus E1 und Verwenden dieses Temporärs als konvertierten Operanden.

Andernfalls (dh wenn E1 oder E2 einen Nichtklassentyp hat oder wenn beide Klassentypen haben, die zugrunde liegenden Klassen jedoch entweder nicht gleich oder eine Basisklasse der anderen sind): E1 kann in E2 konvertiert werden, wenn E1 sein kann implizit in den Typ konvertiert, den der Ausdruck E2 haben würde, wenn E2 in einen r-Wert konvertiert würde (oder in den Typ, den er hat, wenn E2 ein r-Wert ist).

Mit diesem Verfahren wird bestimmt, ob der zweite Operand konvertiert werden kann, um mit dem dritten Operanden übereinzustimmen, und ob der dritte Operand konvertiert werden kann, um mit dem zweiten Operanden übereinzustimmen. Wenn beide konvertiert werden können oder einer konvertiert werden kann, die Konvertierung jedoch nicht eindeutig ist, ist das Programm fehlerhaft. Wenn keiner konvertiert werden kann, bleiben die Operanden unverändert und die weitere Überprüfung wird wie unten beschrieben durchgeführt. Wenn genau eine Konvertierung möglich ist, wird diese Konvertierung auf den ausgewählten Operanden angewendet und der konvertierte Operand wird für den Rest dieses Abschnitts anstelle des ursprünglichen Operanden verwendet.


5.17 / 4

Wenn der zweite und dritte Operand l-Werte sind und denselben Typ haben, ist das Ergebnis von diesem Typ und ist ein l-Wert, und es ist ein Bitfeld, wenn der zweite oder dritte Operand ein Bitfeld ist oder wenn beide Bit- sind. Felder.


5.17 / 5

Andernfalls ist das Ergebnis ein Wert. Wenn der zweite und der dritte Operand nicht denselben Typ haben und entweder einen (möglicherweise lebenslaufqualifizierten) Klassentyp haben, wird die Überlastungsauflösung verwendet, um die Konvertierungen (falls vorhanden) zu bestimmen, die auf die Operanden angewendet werden sollen (13.3.1.2, 13.6). . Wenn die Überlastungsauflösung fehlschlägt, ist das Programm fehlerhaft. Andernfalls werden die so ermittelten Konvertierungen angewendet und die konvertierten Operanden werden für den Rest dieses Abschnitts anstelle der ursprünglichen Operanden verwendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.