Scott Meyers über Rvalueness

8

Ich habe Scott Meyers extrem gesehen ein informatives Video über Universalreferenzen , in dem ich das meiste über Rvalue-Referenzen, Verschieben und Weiterleiten gelernt habe. An einer Stelle sprach er über rvalueness im Gegensatz zum Typ einer Variablen, und er sagte etwas zu dem Effekt von "rvalueness ist unabhängig vom Typ".

>

Ich verstehe, dass Sie eine Methode wie folgt haben können:

%Vor%

und dass hier rRef ein Lvalue ist, weil es identifiziert werden kann, seine Adresse kann genommen werden usw., obwohl sein Typ MyType&& ist.

Aber ein rvalue kann nicht irgendein -Typ sein, oder? Ich meine, es kann nur ein MyType&& sein, oder? In diesem Sinne dachte ich, dass Typ nicht völlig unabhängig von der Wertigkeit ist. Vielleicht vermisse ich etwas.

Aktualisiert: Mein Punkt kann so verdeutlicht werden. Wenn in func() rufe ich eine von zwei überladenen Funktionen auf, die als

definiert sind %Vor%

d. entweder durch Aufrufen von gunc(std::move(rRef)) oder gunc(rRef) scheint der -Typ des resultierenden Ausdrucks in Klammern nicht unabhängig von rvalue zu sein.

    
Kristian D'Amato 20.12.2013, 15:33
quelle

3 Antworten

4

Der -Typ eines Ausdrucks enthält keine Referenzen. Wenn wir also für einen Moment annehmen, dass Referenzen den Referenztyp haben könnten, hätten wir folgendes:

%Vor%

Im obigen Beispiel hätte der Ausdruck a den Typ int und der Ausdruck ra hätte den Typ int& . Ich denke, dass in fast allen Regeln der Spezifikation, die Ausdrücke auf Typen beziehen, zum Beispiel Regeln, die sagen, dass "Ausdruck E vom Typ X sein muss", wir "... oder Verweis auf Typ X" hinzufügen müssten Besetzung Betreiber). Meine Vermutung ist also, dass dies eine zu große Belastung wäre, um nützlich zu sein.

C ++ hat die folgenden Typen

  • statischer Typ eines Ausdrucks

Nur "Typ des Ausdrucks" genannt (wenn nicht anders angegeben, dass der dynamische gemeint ist). Dies ist eine Eigenschaft von Ausdrücken, die den Typ von Ausdrücken angeben, die zum Zeitpunkt der Kompilierung von dem entfernt werden, worauf der Ausdruck verweist. Wenn sich beispielsweise a auf eine int& oder int Variable bezieht oder ein Literal 0 ist, haben alle diese Ausdrücke den Typ int .

  • dynamischer Typ eines lvalue-Ausdrucks

Dies ist der Typ, auf den sich das Nicht-Basisklassenobjekt bezieht, auf das sich ein lvalue-Ausdruck bezieht.

%Vor%

% os hat in diesem Fall den statischen Typ ostream und den dynamischen Typ ofstream .

  • Typ eines Objekts oder einer Referenz

Dies ist der Typ, den ein Objekt oder eine Referenz tatsächlich hat. Ein Objekt hat immer einen einzigen Typ und sein Typ ändert sich nie. Aber welches Objekt an welchem ​​Ort existiert, ist nur zur Laufzeit bekannt, also ist "Typ eines Objekts" in der Regel auch eine Laufzeitsache

%Vor%

Der Typ des Objekts, das mit *os (und dem dynamischen Typ des lvalue *os aswell) bezeichnet wird, ist nur zur Laufzeit bekannt

%Vor%

Hier ist der Typ des Arrays, das durch den Operator new erstellt wurde, nur zur Laufzeit bekannt und kann (hoffentlich) auch nicht in das statische C ++ - Typsystem übergehen. Die berüchtigte Aliasing-Regel (die es verbietet, Objekte aus inkompatiblen Werten zu lesen) spricht von "dynamischem Typ" von Objekten, vermutlich weil sie hervorheben möchte, dass Runtime-Interessen von Interesse sind. Streng genommen sagt man aber "dynamischer Typ" eines Objekts, weil ein Objekt keinen "statischen Typ" hat.

  • Deklarierter Typ einer Variablen oder eines Members

Dies ist der Typ, den Sie in einer Deklaration angegeben haben. In Bezug auf den Typ eines Objekts oder den Typ eines Ausdrucks kann dies manchmal subtil anders sein

%Vor%

Hier hat der Ausdruck a->a den Typ const int , aber der deklarierte Typ des Elements, für das a->a aufgelöst wurde, hat den Typ int (die Element-Entität). Der Typ des mit a->a gekennzeichneten Objekts hat den Typ const int , weil wir ein const A -Objekt mit dem Ausdruck new erstellt haben und daher alle nicht statischen Datenelemente implizit const Unterobjekte sind. In va->a hat der Ausdruck den Typ volatile const int a , und der deklarierte Typ der Variablen hat weiterhin den Typ int und der Typ des Objekts, auf das verwiesen wird, hat immer noch den Typ const int .

Wenn Sie "type of a" sagen und Sie "a" als "int & amp; a;" Sie müssen also immer sagen, was Sie mit "Typ von a" meinen. Meinst du den Ausdruck? Dann hat "a" den Typ int . Es kann sogar noch fieser werden. Stellen Sie sich "int a [10];" vor. Hier hat der Ausdruck "a" den Typ int* oder int[10] abhängig davon, ob Sie die Umwandlung des Arrays in Zeiger in Ihrem Ausdruck berücksichtigt haben oder nicht, wenn Sie nach dem "Typ von a" fragen. Wenn Sie nach dem Typ der Variablen fragen, auf die sich "a" bezieht, lautet die Antwort eindeutig int bzw. int[10] .

Von welchem ​​Typ kann ein Wert sein? Rvalues ​​sind Ausdrücke.

%Vor%

Hier haben wir die rvalues 0 und std::move(x) . Beide Rvalues ​​haben den Typ int . Der Ausdruck x , der im Initialisierer für z erscheint, ist ein Lvalue, obwohl er sich auf dasselbe Objekt bezieht, auf das sich der Rvalue std::move(x) bezieht.

Ihr letzter Punkt in Ihrer Frage zu der überladenen Funktion, die mit einem rvalue bzw. lvalue aufgerufen wird, ist interessant. Nur weil rvalue-Referenzen als int && geschrieben werden, bedeutet das nicht, dass rvalues ​​den Typ int haben. Sie werden rvalue-Referenzen genannt, weil Sie sie mit rvalues ​​initialisieren können und die Sprache diese Initialisierung bevorzugt, anstatt eine lvalue-Referenz mit einem rvalue zu initialisieren.

Es kann auch nützlich sein, Ausdrücke in Name-Form zu sehen, die rvalues ​​

sind %Vor%

Wenn Sie X oder Y verwenden, handelt es sich um R-Werte. Aber diese Fälle sind die einzigen, an die ich denken kann.

    
Johannes Schaub - litb 21.12.2013, 01:29
quelle
2

Ich glaube, Sie haben einen Teil seines Zitats aufgegeben:

  Ein letzter Punkt ist es wert, im Auge zu behalten: die Wert- oder R-Wertigkeit   eines Ausdrucks ist unabhängig von seinem Typ.

Er erklärt es hier . Sein Hauptpunkt war dies:

  

Der Typ eines Ausdrucks sagt Ihnen nicht, ob es ein Lvalue oder ist   ein rvalue.

Und in seinen abschließenden Bemerkungen:

  

In einer Typdeklaration gibt "& amp; & amp;" entweder eine R-Wert-Referenz oder a an   Universalreferenz - eine Referenz, die zu einem Lvalue aufgelöst werden kann   Referenz oder ein Referenzwert. Universalreferenzen haben immer die   Form T & amp; & amp; für einige abgeleitete Typ T.

     

Referenzkollaps ist der Mechanismus, der zu universellem führt   Referenzen (das sind wirklich nur rvalue Referenzen in Situationen   Wenn Referenz-Kollaps stattfindet, wird manchmal in lvalue aufgelöst   Referenzen und manchmal zu rvalue Referenzen. Es tritt in angegeben auf   Kontexte, in denen Verweise auf Referenzen während der Kompilierung auftreten können.   Diese Kontexte sind Vorlagenabzüge, automatische Typabzüge,   typedef formation und use, und declype Ausdrücke.

Der Typ T wird hier verwendet, um any -Typ zu bedeuten. Es könnte int&& oder double&& oder Widget&& sein - es ist egal.

    
Zac Howland 20.12.2013 15:40
quelle
0

Lassen Sie uns zuerst die Diskussion auf einfache rvalue-Referenzen beschränken und universelle Referenzen beiseite lassen. Wir sprechen nicht von template <typename T> ... T &&var ...

Soweit reguläre Type &&var = somevalue; -Referenz von rvalue, denke ich, die Bedeutung davon ist:

Was mit dieser Referenz verbunden ist, war "wegwerfbar", wenn es an die Referenz gebunden war. Es ging außer Reichweite. Wenn du es in dem Moment änderst, in dem du es gebunden hast, würde es niemand erfahren. Es gab keine anderen Hinweise darauf, als es gebunden war.

Dies erlaubt uns, einige Freiheiten mit rvalue-Referenzen zu nehmen, die wir nicht mit anderen Arten von Variablen aufnehmen können. Die erste Anwendung, die einem einfällt, ist, seinen Inhalt mit swap () zu stehlen.

    
Arkadiy 20.12.2013 18:33
quelle