Kann reinterpret_cast (oder eine beliebige Umwandlung) xvalues ​​in lvalues ​​umwandeln?

8

Ist der folgende Code zulässig (nach C ++ 11 und / oder C ++ 14 Standard (en))?

%Vor%
  • Wenn ja, ist das Verhalten nicht definiert?
  • Wenn es nicht undefiniertes Verhalten ist, kann ich sogar a in foo mutieren, ohne dass es UB wird?

Es kompiliert auf clang 3.5, nicht auf gcc 4.9. GCC-Fehler:

%Vor%

BEARBEITEN

FYI, eine benutzerdefinierte Cast, die weniger behaart ist als die vorherige, und die auf C ++ 11 sowohl für GCC als auch für Clang funktioniert, wäre die folgende Funktion lvalue :

%Vor%     
pepper_chico 07.11.2014, 02:30
quelle

3 Antworten

6

Update: Der Code ist in C ++ 11 schlecht formatiert. Die Antwort unten ist für C ++ 14. Siehe Hinweis am Ende dieser Antwort.

Ich glaube, dass dieser Code sowohl wohlgeformt als auch gut definiert ist. Hier ist der Grund.

Das Ergebnis von std::move ist ein xvalue [1], was eine Art von glvalue ist; und das Konvertieren eines gl-Wertes in eine l-Wert-Referenz mit reinterpret_cast scheint durch den Wortlaut des Standards erlaubt zu sein:

  

Ein glvalue-Ausdruck vom Typ T1 kann in den Typ "Verweis auf T2 " umgewandelt werden, wenn ein Ausdruck vom Typ "pointer" verwendet wird   to T1 "kann mit einem T2 explizit in den Typ" pointer to reinterpret_cast "konvertiert werden. Das Ergebnis bezieht sich   zu dem gleichen Objekt wie der Quell-gl-Wert, aber mit dem angegebenen Typ. [Anmerkung: Das ist für lvalues ​​eine Referenz   Cast reinterpret_cast<T&>(x) hat den gleichen Effekt wie die Conversion *reinterpret_cast<T*>(&x) mit   die integrierten Operatoren & und * (und ähnlich für reinterpret_cast<T&&>(x) ). - Endnote] Keine temporäre   wird erstellt, es wird keine Kopie erstellt und Konstruktoren (12.1) oder Konvertierungsfunktionen (12.3) werden nicht aufgerufen.73

Da "pointer to int " in "pointer to int " konvertiert werden kann, ist auch dieses reinterpret_cast erlaubt. Der Standard sagt nichts darüber aus, ob der Zieltyp eine lvalue-Referenz oder eine rvalue-Referenz sein muss.

Das Ergebnis der Umwandlung ist durch den obigen Absatz klar definiert: Es bezieht sich auf das gleiche Objekt wie der Quell-glvalue --- also ein temporäres int -Objekt mit dem Wert 5 . ([dcl.init.ref] gibt an, dass ein temporäres Objekt erstellt wird, wenn ein prvalue an eine Referenz gebunden ist.)

Der Zugriff auf den Wert über int& verletzt auch keine Aliasregeln, da das ursprüngliche Objekt auch vom Typ int war. Tatsächlich glaube ich, dass es sogar gut definiert wäre, das Temporäre durch den so erhaltenen Wert zu modifizieren.

Hinweis: In der C ++ 11-Formulierung steht "lvalue expression", nicht "glvalue expression". Die Formulierung mit "glvalue-Ausdruck" stammt aus N3936, dem letzten Arbeitsentwurf für C ++ 14. Ich bin kein Experte dafür, wie der Standardisierungsprozess funktioniert, aber ich glaube, das bedeutet, dass die Änderung von "lvalue" zu "glvalue" bereits vom Komitee abgestimmt wurde und wenn ISO den C ++ 14-Standard veröffentlicht, wird es gehen ziemlich ähnlich zu dem sein, was es oben sagt.

[1] Außer in dem seltenen Fall, in dem das Argument eine Funktion ist; In diesem Fall ist das Ergebnis ein Lvalue, da es keine Funktion rvalues ​​gibt.

    
Brian 07.11.2014, 02:53
quelle
5

Das Problem wird aktiviert, wenn reinterpret_cast xvalues ​​in lvalues ​​konvertieren darf. Im Gegensatz zu dem, was andere einfügen, erwähnt der relevante Absatz (5.2.10.11) nur lvalues:

  

Ein lvalue-Ausdruck vom Typ T1 kann in den Typ "reference to" umgewandelt werden   T2 "wenn ein       Ausdruck vom Typ "Zeiger auf T1" kann explizit in den Typ konvertiert werden       "Pointer to T2" mit einem reinterpret_cast ..

Es gibt einen Vorschlag von Michael Wong, den Wortlaut in glvalue zu ändern, aber es scheint eine Wand zu treffen:

Ссылка

Ich nehme an, dass dies bedeutet, dass die Konvertierung ab sofort nicht legal ist, da sie nur die Konvertierung von lvalues ​​erlaubt.

    
hs_ 07.11.2014 03:11
quelle
1

Der relevante Abschnitt in der Norm ist 5.2.10 [expr.reinterpret.cast]. Es gibt zwei relevante Absätze:

Erstens gibt es Absatz 1, der endet in:

  

Keine andere Konvertierung kann explizit mit reinterpret_cast durchgeführt werden.

... und Absatz 11, da keiner der anderen gilt:

  

Ein glvalue-Ausdruck vom Typ T1 kann in den Typ "Verweis auf T2 " umgewandelt werden, wenn ein Ausdruck vom Typ "Zeiger auf T1 " explizit in den Typ "Zeiger auf T2 " konvertiert werden kann mit einem reinterpret_cast . Das Ergebnis bezieht sich auf das gleiche Objekt wie der Quell-gl-Wert, aber mit dem angegebenen Typ. [Anmerkung: Das heißt, für lvalues ​​hat eine Referenzbesetzung reinterpret_cast<T&>(x) den gleichen Effekt wie die Konversion *reinterpret_cast<T*>(&x) mit den integrierten Operatoren & und * (und ähnlich für reinterpret_cast<T&&>(x)) . - Endnote) Kein Temporär wird erstellt, es wird keine Kopie erstellt und Konstruktoren (12.1) oder Konvertierungsfunktionen (12.3) werden nicht aufgerufen.

Alle anderen Klauseln gelten nicht für Objekte, sondern nur für Zeiger, Zeiger auf Funktionen usw. Da ein rvalue kein glvalue ist, ist der Code illegal.

    
Dietmar Kühl 07.11.2014 02:50
quelle