Ist die Verwendung von std :: forward mit auto && das Richtige zu tun

7

Versuchen Sie zu verstehen, ob die Verwendung von std::forward mit auto&& Variablen der richtige Weg ist, um diese Variablen zu übergeben, damit sie verschoben werden können.

Angenommen, es gibt eine Funktion:

%Vor%

Und der Aufrufer - zwei Variablen, die auf rvalue und lvalue verweisen:

%Vor%

Wir wissen, dass eine Variable vom Typ auto&& eine universelle Referenz ist, weil eine Typableitung stattfindet. Das bedeutet, dass sowohl uniRefRV als auch uniRefLV universelle Referenzen sind.

In meinem Beispiel ist es offensichtlich, dass uniRefRV ist rvalue und uniRefLV ist lvalue , aber konzeptionell sind sie beide universelle Referenzen und Wenn die Definition anders ist, könnten sie entweder rvalue oder lvalue darstellen.

Nun möchte ich moveWidget() aufrufen und diese universellen Verweistypen perfekt weiterleiten. Die Richtlinie (von Scott Meyers) sagt:

  

Übergeben und geben Sie rvalue-Referenzen über std::move , universelle Referenzen über std::forward zurück.

Und wenn ich die Richtlinie nicht völlig falsch interpretiere, scheint es logisch, std::forward zu verwenden. Aber lassen Sie uns alle möglichen Entscheidungen in Betracht ziehen:

%Vor%

Glauben Sie, wir sollten beide Referenzen uniRefLV und uniRefRV gleich behandeln und welche von drei Optionen sollten wir für eine perfekte Weiterleitung verwenden?

    
Sereger 02.07.2013, 15:02
quelle

4 Antworten

11

Sie missdeuten die Leitlinie. Oder es zumindest zu wörtlich nehmen.

Ich denke, es gibt drei wichtige Dinge, die hier zu realisieren sind.

Zunächst sind hier alle Typen bekannt. Der Hinweis für universelle Referenzen gilt meist für generischen Code mit Vorlagen, bei denen Sie gar nicht wissen, ob etwas eine lvalue-Referenz oder eine rvalue-Referenz ist oder nimmt.

Zweitens nimmt die Funktion eine rvalue-Referenz: Sie müssen einen rvalue übergeben. Zeitraum. Es gibt keine Wahl hier.

Und die logische Schlussfolgerung ist, dass Sie nicht eine universelle Referenz übergeben möchten: Was immer Sie übergeben, muss ein rvalue sein, es kann niemals ein lvalue sein. Universelle Referenzen können lvalues ​​sein (wenn sie als lvalue-Referenzen instanziiert werden). Eine universelle Referenz zu übergeben heißt: "Ich weiß nicht, um was für eine Referenz es sich handelt, und ich kann sie entweder als rvalue oder als lvalue übergeben, also gebe ich sie genau so weiter, wie ich sie bekommen habe". Der Fall in dieser Frage ist eher wie "Ich weiß genau, was ich passieren muss, also das werde ich weitergeben".

    
R. Martinho Fernandes 02.07.2013, 15:40
quelle
4

Nehmen wir an, dass der Fall nicht so simpel ist wie es scheint. Anstelle von giveMeInt haben wir etwas ähnliches:

%Vor%

Und anstelle von moveMe haben Sie etwas, das tatsächlich eine universelle Referenz benötigt und daher kein unbedingtes std::move benötigt.

%Vor%

Jetzt brauchen Sie eigentlich eine perfekte Weiterleitung.

%Vor%     
Sebastian Redl 02.07.2013 15:56
quelle
4

Ich glaube, du bist hier ein wenig durcheinander geraten. Weder uniRefLV noch uniRefRV sind "universelle Referenzen". Sie sind nur Variablen und beide haben einen bestimmten Typ. Beide Typen sind Referenztypen, aber das ist nicht wichtig.

Um moveWidget aufzurufen, müssen Sie in beiden Fällen std::move verwenden, und in beiden Fällen lautet die Semantik, dass nach dem Verschieben Ihr ursprüngliches w verschoben wurde und sich somit nicht mehr in einem bestimmten Zustand befindet . (Das Beste, was Sie damit tun können, ist es entweder zu zerstören oder neu zuzuweisen.)

Im Gegensatz dazu ist eine echte universelle Referenz ein Template-Argument und muss immer die kanonische Form haben:

%Vor%

Das ist

  • Die Vorlage ist eine Funktion Vorlage,
  • Der Template-Parameter T tritt als Funktionsparameter T && und
  • auf
  • Das Template-Argument der aktualisierten Spezialisierung wird abgeleitet .

Wenn diese Bedingungen erfüllt sind, ist T && die universelle Referenz, und Sie übergeben sie über std::forward<T> , wodurch der korrekte Referenztyp erhalten bleibt.

    
Kerrek SB 05.07.2013 21:08
quelle
0

(1) .a  Es verletzt nicht die Richtlinie. Sie sagen ausdrücklich "ich brauche diesen Wert nicht mehr, also nimm ihn" das ist im Grunde, wofür std :: move ist. Und wenn du weiterhin uniRefLV verwendest, nachdem du std :: move gemacht hast - das ist gegen die Richtlinie. Der Wert ist (möglicherweise) verschwunden, Sie haben ihn übergeben. Das sagt Scott .

(1) .b  Das ist echt. Obwohl std :: move dort nicht benötigt wird, vereinfacht es das Lesen des Quellcodes.

(2), (3)  Wir müssen kein Template-Argument für std :: forward angeben. Es soll von einem Compiler abgeleitet werden! Zumindest ist das mein Verständnis. Und ich sehe keinen Sinn, es manuell zu machen. Weiterleiten Entfernen Sie einfach die Referenzen (& amp; & amp; und & amp;) von der Typdefinition. Deshalb wird forwarding genannt und für die Datenweiterleitung verwendet. Wenn Sie eine Funktion haben:     Vorlage     void foo (T & amp; & tt;);

Es kann zu einem der folgenden Elemente instanziiert werden:

%Vor%

Zum Beispiel:

%Vor%

Wird instanziiert:

%Vor%

Nicht:

%Vor%

Also werden alle diese Referenzen durch einen std :: forward entfernt. Und nur *modificator* SomeType t wird der Funktion innerhalb von foo() übergeben, an die sie übergeben wird.

    
GreenScape 10.07.2013 08:34
quelle

Tags und Links