Frage

class Foo {
  public:
  explicit Foo(double item) : x(item) {}

  operator double() {return x*2.0;}

  private:
  double x;
}

double TernaryTest(Foo& item) {
  return some_condition ? item : 0;
}

Foo abc(3.05);
double test = TernaryTest(abc);

Im obigen Beispiel, warum ist Test gleich 6 (statt 6,1), wenn some_condition wahr ist?

Ändern Sie den Code wie unten Wert von 6,1 zurückgibt

double TernaryTest(Foo& item) {
  return some_condition ? item : 0.0; // note the change from 0 to 0.0
}

Es scheint, dass (im ursprünglichen Beispiel) der Rückgabewert von Foo :: operator double in einen int gegossen wird und dann wieder zu einem Doppel. Warum?

War es hilfreich?

Lösung

Der bedingte Operator prüft Umwandlungen in beiden Richtungen. In diesem Fall, da Ihr Konstruktor explizit (so die ?: nicht eindeutig ist), wird die Umwandlung von Foo zu int verwendet, Ihre Conversion-Funktion, die double konvertiert: Das funktioniert, weil nach der Konvertierungsfunktion Anwendung, eine Standard-Konvertierung, die folgt konvertiert die double (truncation) int. Das Ergebnis ?: in Ihrem Fall ist int, und hat den Wert 6.

Im zweiten Fall wird, da der Operand double hat geben, keine solche Umwandlung zu nachlauf int erfolgt und damit der Ergebnistyp ?: hat mit dem erwarteten Wert eingeben double.

Um die „unnötige“ Conversions zu verstehen, müssen Sie verstehen, dass Ausdrücke wie Ihre ?: ausgewertet werden „kontextfrei“: Wenn Sie den Wert und die Art der es zu bestimmen, wird der Compiler nicht der Ansicht, dass es der Operand eines return ist eine Funktion, die ein double.


Edit: Was passiert, wenn Ihr Konstruktor ist implizite ? Der ?: Ausdruck wird nicht eindeutig sein, weil Sie eine int zu einem R-Wert vom Typ Foo umwandeln können (mit dem Konstruktor) und einem Foo zu rvalue vom Typ int (mit der Konvertierungsfunktion). Die Norm sagt

  

Mit diesem Verfahren wird bestimmt, ob der zweite Operand umgewandelt werden kann, den dritten Operanden zu entsprechen, und ob der dritte Operand kann umgewandelt werden, um den zweiten Operanden übereinstimmen. Wenn beide umgewandelt werden kann, oder man kann umgewandelt werden, aber die Umwandlung ist nicht eindeutig, wird das Programm schlecht ausgebildet ist.


Die Absätze erläutern, wie Ihre Foo zu int umgewandelt wird:

5.16/3 über condition ? E1 : E2:

  

Andernfalls, wenn der zweite und der dritte Operand haben verschiedene Arten, und entweder hat (möglicherweise cv-qualifiziert) Klasse-Typ, wird versucht, jede dieser Operanden auf den Typ des anderen zu konvertieren. [...] kann E1 umgewandelt werden E2 zu entsprechen, wenn E1 kann implizit in den Typ umgewandelt werden, dass die Expression E2 hätte, wenn E2 mit einem R-Wert umgewandelt wurden (oder die Art hat, wenn E2 ist ein R-Wert).

4.3 über "implizit konvertiert":

  

Ein Ausdruck e kann implizit in einen Typ T umgewandelt werden, wenn und nur wenn die Erklärung T t = e; wohlgeformt ist, für einige temporäre Variable t erfunden.

8.5/14 über Kopie Initialisierung (T t = e;)

  

Wenn der Quelltyp ist ein (möglicherweise cv-qualifiziert) Klasse-Typ, Konvertierungsfunktionen berücksichtigt werden. Die anwendbaren Umrechnungsfunktionen sind aufgezählt (13.3.1.5), und die beste eine wird durch Überladungsauflösung gewählt (13.3). Die benutzerdefinierte Umwandlung sogenannte ausgewählt wird, um den Initialisierer Ausdruck in das Objekt zu konvertieren initialisiert. Wenn die Konvertierung nicht durchgeführt werden kann oder nicht eindeutig ist, wird die Initialisierung schlecht ausgebildet ist.

13.3.1.5 über die Konvertierungsfunktion Kandidaten

  

Die Konvertierungsfunktionen von S und seinen Basisklassen berücksichtigt. Diejenigen, die nicht versteckt sind innerhalb von S und T Ausbeute Typ oder einem Typ, der umgerechnet werden kann T über eine Standard-Umwandlungsfolge eingeben (13.3.3.1.1) sind Kandidaten Funktionen.

Andere Tipps

Dies wird in positiv verwirrend Detail in Abschnitt 5.16 der Norm. Der wichtigste Teil ist in Absatz 3 „Wenn E2 ein L-Wert ist: E1 E2 umgerechnet werden können, wenn E1 entsprechen können (Abschnitt 4) auf die Art implizit konvertiert werden Bezugnahme auf T2 ', vorbehaltlich der Einschränkung, dass bei der Umwandlung der Referenz muss binden direkt (8.5.3) zu E1. "

In dem Ausdruck, der nur lvalue item, so ist die Frage, ob 0 (int) kann implizit eingeben Foo umgewandelt werden. In diesem Fall gibt es keine implizite Konvertierung eines anderen Typs zu einem Foo, da die einzigen verfügbaren Konvertierungsfunktion explicit markiert. Daher funktioniert das nicht, und wir folgen mit „wenn E2 ein R-Wert ist, oder wenn die obige Umwandlung nicht durchgeführt werden kann:“ (über den Teil übersprungen, wenn sie beiden Klassentyp haben) „Im anderen Fall (dh wenn E1 oder E2 hat einen nonclass Typ, oder wenn sie aber die zugrunde liegenden Klassen sind nicht entweder gleich oder eine Basisklasse der anderen beiden Klassentypen haben): E1 umgewandelt werden kann E1 entsprechen, wenn E1 kann implizit in den Typ umgewandelt werden, dass die Expression E2 haben würde, wenn E2 mit einem R-Wert umgewandelt wurde (oder die Art hat, wenn E2 ist ein R-Wert). "

Daher sehen wir, dass 0 ein R-Wert vom Typ int. Wir können eine Foo konvertieren, da wir implizit eine Foo zu einem double und von dort zu einem int umwandeln können. Dann gilt:

"Mit diesem Verfahren wird bestimmt, ob der zweite Operand umgewandelt werden kann, den dritten Operanden zu entsprechen, und ob der dritte Operand umgewandelt werden kann, den zweiten Operanden übereinstimmen. Wenn beide umgewandelt werden können, oor man kann umgewandelt werden, aber die Umwandlung ist nicht eindeutig, wird das Programm schlecht gebildet. Wenn weder umgerechnet werden können, werden die Operanden bleiben unverändert und eine weitere Überprüfung wird wie unten beschrieben durchgeführt. Wenn genau eine Umwandlung möglich ist, dass die Umstellung auf den gewählten Operanden und des umgewandelten angewendet wird Operand wird an der Stelle des ursprünglichen Operanden für den Rest dieses Abschnitts verwendet. "

Da wir ein Foo zu einem int umwandeln können, wandeln wir die Foo zu einem int für den Rest der Bestimmung. Wir haben jetzt zwei ints als Ausdrucksarten erhalten, und mindestens eine ist ein R-Wert.

I Absatz gehen kann 5 und 6, aber ich denke, es ist ziemlich offensichtlich, dass der Ausdruck int Typen hat.

Ich denke, die Imbissbuden sind:

  1. Ihr Compiler arbeitet nach dem Standard.

  2. Die Regeln für die Art eines bedingten Ausdrucks sind zu kompliziert leicht erlernt zu werden. Nicht schieben Sie den Umschlag, weil man irgendwann einen Fehler machen werde. (Außerdem ist dies genau die Art von Ort, wo ein Compiler scheitern könnte den Standard genau zu implementieren.)

  3. Versuchen Typen angeben, so dass sowohl der zweite und dritte Ausdruck des gleichen Typs ist. Auf jedem Fall versuchen, Ausdrücke zu vermeiden, die nicht von dem gewünschten Typ ist.

Der Typ des ternären Ausdruck wird zur Compile-Zeit bestimmt; es ist egal, welche some_condition zur Laufzeit ist.

Ich denke, die Frage ist dann: warum wählen die Compiler int statt Doppel im ersten Beispiel?

ternärer Operator errät den Typen von seinen Argumenten. es kann nicht Artikel umwandeln in int, aber es kann Artikel umwandeln zu verdoppeln, die es dann zu int konvertiert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top