Bedingte Operator-Typ-Konvertierung in VS 2012

8

Ich konvertiere gerade ein ziemlich großes Projekt von VS 2008 nach 2012 und habe ein Problem mit einer Änderung in der Art und Weise, in der die konditionale Operator-Typ-Konvertierung durchgeführt zu werden scheint, ausgeführt.

Lassen Sie mich damit beginnen, dass ich akzeptiere, dass die Semantik des Bedingungsoperators etwas kompliziert ist und realisiere, was der Code ursprünglich gemacht hat, ist wahrscheinlich nicht korrekt, aber ich bin wirklich verwirrt, was jetzt in VS 2012 passiert und ich frage mich wenn jemand genau erklären kann, warum es tut, was es tut.

%Vor%

In VS 2008 würde der obige bedingte Operator dazu führen, dass die Methode operator const wchar_t*() für ds aufgerufen wird und der Assert nicht ausgelöst wird. Das heißt, es würde implizit ds auf ein const wchar_t* übertragen.

In VS 2012 führt der bedingte Operator zu folgendem Verhalten:

  • Ein temporäres DummyString wird über den Kopierkonstruktor
  • erstellt
  • Die Umwandlung in const wchar_t* wird dann für diese temporäre Kopie ausgeführt

Dies führt dazu, dass pPtr auf ein zerstörtes Objekt verweist und die Assertion natürlich ausgelöst wird.

Wenn ich nun den DummyString(int) -Konstruktor aus der Klasse entferne, kann der Code in VS2012 nicht kompiliert werden ( keine Konvertierung von 'DummyString' nach 'int' ), also eindeutig 0 in der Bedingung bewirkt, dass der Ausdruck als ein int und nicht als ein Zeiger ausgewertet wird.

Aber in diesem Fall, warum wird nicht der DummyString(int) -Konstruktor aufgerufen, um die 0 in ein DummyString zu konvertieren? Und warum erstellt der Compiler eine Kopie von ds und wirft diese dann nach wchar_t *, wenn er genauso gut das ursprüngliche Objekt bearbeiten könnte?

Ich würde gerne erleuchtet sein! :)

    
Jonathan Potter 29.05.2013, 08:15
quelle

2 Antworten

13

In Absatz 5.16 / 3 des C ++ 11-Standards heißt es:

  

[...] wenn der zweite und der dritte Operand unterschiedliche Typen haben und eine (möglicherweise cv-qualifizierte) Klasse haben   type oder wenn beide glvalues ​​der gleichen Wertkategorie und des gleichen Typs außer cv-qualification sind, an   Es wird versucht, jeden dieser Operanden in den Typ des anderen zu konvertieren. [...]

Und das:

  

[...] Wenn beides sein kann   konvertiert, oder man kann konvertiert werden, aber die Konvertierung ist mehrdeutig, das Programm ist schlecht gebildet. Wenn nicht   kann konvertiert werden, die Operanden bleiben 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 und den konvertierten angewendet   Operand wird anstelle des ursprünglichen Operanden für den Rest dieses Abschnitts verwendet.

In diesem Fall ist nur die Konvertierung von int nach DummyString möglich, da in der anderen Richtung ein const wchar_t* so weit wie möglich geht - es gibt keine standardmäßige implizite Konvertierung von const wchar_t* nach ein int .

Aus diesem Grund beschwert sich der Compiler, wenn Sie den Konvertierungskonstruktor entfernen, der ein int übernimmt: Wenn dieser Konstruktor nicht existiert, wäre das Programm gemäß dem obigen Absatz schlecht formatiert (es gibt keine Konvertierung in beide Richtungen) .

Daher wird der zweite Operand (die erste Wahl) als DummyString(0) betrachtet.

Die Tatsache, dass der zweite Operand in ein DummyString konvertiert werden kann, bedeutet jedoch nicht , dass der zweite Operand überhaupt ausgewertet wird. Das hängt von der Bedingung ab, und die Bedingung wird als false ausgewertet (es sei denn, Sie übergeben 100 Argumente an die Befehlszeile). Dies erklärt, warum Sie keinen Aufruf dieses Konstruktors sehen. Zu Ziffer 5.16 / 1:

  

Bedingte Ausdrücke gruppieren von rechts nach links. Der erste Ausdruck wird kontextabhängig in bool umgewandelt (Abschnitt 4).   Es wird ausgewertet und wenn es wahr ist, ist das Ergebnis des bedingten Ausdrucks der Wert des zweiten Ausdrucks,   andernfalls das des dritten Ausdrucks. Nur einer der zweiten und dritten Ausdrücke wird ausgewertet. [...]

Aber warum scheitert Ihre Behauptung?

  

Und warum erstellt der Compiler eine Kopie von ds und wirft diese dann auf wchar_t* , wenn er genauso gut das ursprüngliche Objekt transformieren könnte?

Nun, das liegt an den Absätzen 5.16 / 4-5:

  

Wenn der zweite und dritte Operand glvalues ​​der gleichen Wertkategorie sind und denselben Typ haben, [...]

     

Andernfalls ist das Ergebnis ein prvalue . Wenn der zweite und der dritte Operand nicht denselben Typ haben, und auch nicht   hat (möglicherweise cv-qualifiziert) Klassentyp, Überladungsauflösung wird verwendet, um die Konvertierungen (falls vorhanden) zu bestimmen   angewendet auf die Operanden (13.3.1.2, 13.6).

0 ist kein glvalue, daher ist das Ergebnis der Bedingung ein prvalue. Dies bedeutet, dass die Bewertung des bedingten Operators, wenn die Bedingung false ist, am Ende ein temporäres aus ds ergibt, was das beobachtete Verhalten ist.

Dies wird durch Absatz 5.16 / 6 vorgeschrieben, der besagt:

  

Lvalue-to-rvalue (4.1), Array-to-Pointer (4.2) und Funktion-zu-Zeiger (4.3) Standard-Konvertierungen werden durchgeführt   auf dem zweiten und dritten Operanden. Nach diesen Conversions muss einer der folgenden Werte enthalten sein:

     

- Der zweite und der dritte Operand haben den gleichen Typ; Das Ergebnis ist von diesem Typ. Wenn die Operanden haben   Klassentyp, das Ergebnis ist ein prvalue temporary des Ergebnistyps, der von beiden initialisiert wird   der zweite Operand oder der dritte Operand abhängig vom Wert des ersten Operanden. [...]

Die Bedingung '" der zweite und der dritte Operand haben denselben Typ ", weil die Operanden jetzt nach der in 5.16 / 3 beschriebenen Konvertierung betrachtet werden (siehe der Anfang dieser Antwort).

Um Ihr Problem zu beheben, können Sie eine explizite Umwandlung für das zweite Argument durchführen:

%Vor%

Da eine Standardkonvertierung von 0 in einen Zeigertyp existiert und zu einem Nullzeiger führt - siehe Abschnitt 4.10 / 1.

    
Andy Prowl 29.05.2013, 08:36
quelle
0

Wenn Sie möchten, können Sie vermeiden, den Kopierkonstruktor aufzurufen, indem Sie Folgendes angeben:

%Vor%     
raj raj 29.05.2013 08:50
quelle