Vorteile der Verwendung von vorwärts
In perfect forwarding, std::forward
wird verwendet, um zu konvertieren, die mit Namen rvalue-Referenzen t1
und t2
Unbenannte rvalue-Referenzen. Was ist der Zweck, dies zu tun? Wie würde sich das auf die aufgerufene Funktion inner
wenn wir verlassen t1
& t2
als lvalues?
template <typename T1, typename T2>
void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
InformationsquelleAutor der Frage Steveng | 2010-08-27
Du musst angemeldet sein, um einen Kommentar abzugeben.
Verstehen Sie die forwarding-problem. Sie können Lesen Sie das gesamte problem im detailaber ich fasse.
Grundsätzlich, da der Ausdruck
E(a, b, ... , c)
wollen wir den Ausdruckf(a, b, ... , c)
gleichwertig. In C++03 ist dies unmöglich. Es gibt viele versuche, aber Sie alle Versagen in mancher Hinsicht.Die einfachste ist die Verwendung eines lvalue-Referenz:
Aber auf diese Weise nicht behandeln vorläufige Werte:
f(1, 2, 3);
als diejenigen, die nicht gebunden werden kann, um eine lvalue-Referenz.Den nächsten Versuch könnte sein:
Die behebt das oben genannte problem, aber flips-flops. Es jetzt nicht zulässt
E
zu nicht-const-Argumenten:Dem Dritten Versuch akzeptiert const-Referenzen, aber dann
const_cast
's dieconst
Weg:Dieser akzeptiert alle Werte, die übergeben werden können über alle Werte, die aber möglicherweise führt zu undefiniertem Verhalten:
Einer endgültigen Lösung verarbeitet alles richtig...auf Kosten des seins unmöglich zu pflegen. Sie bieten überlastungen
f
mit alle Kombinationen von const und non-const:N Argumente verlangen, 2N - Kombinationen, ein Alptraum. Wir möchten, dies zu tun automatisch.
(Das ist effektiv das, was wir bekommen, dass der compiler für uns in C++11.)
In C++11, wir bekommen eine chance, dies zu beheben. Eine Lösung modifiziert Vorlage Abzug-Regeln auf die vorhandenen Arten, aber diese potenziell Pausen viel code. Also müssen wir einen anderen Weg finden.
Die Lösung ist, verwenden Sie stattdessen den neu hinzugefügten rvalue-Referenzen; wir können uns neue Regeln einzuführen, wenn die Aufzucht rvalue-Referenz-Typen und erstellen Sie jedes gewünschte Ergebnis. Wir können schließlich nicht möglicherweise brechen code jetzt.
Wenn eine Referenz auf eine Referenz (Hinweis: Bezug ist ein umfassender Begriff bedeutet sowohl
T&
undT&&
), verwenden wir die folgende Regel, um herauszufinden, die resultierenden Typ:Oder in tabellarischer form:
Nächsten, mit template-argument Abzug: wenn ein argument ist ein lvalue Ein, wir liefern die Vorlage-argument mit einer lvalue Referenz auf A. Sonst leiten wir in der Regel. Diese so genannte universal Verweise (der Begriff Weiterleitung Referenz ist nun der offizielle).
Warum ist dies nützlich? Da kombinierten wir pflegen die Fähigkeit zu verfolgen, den Wert der Kategorie der Art: wenn es ein lvalue, wir haben eine lvalue-Referenz parameter, sonst haben wir eine rvalue-Referenz parameter.
Code:
Das Letzte, was ist "weiterleiten" der Wert der Kategorie der Variablen. Halten Sie im Verstand, sobald Sie in der Funktion den parameter weitergegeben werden konnten als lvalue um nichts:
Ist das nicht gut. E braucht, um die gleiche Art von Wert-Kategorie, die wir haben! Die Lösung ist diese:
Was macht das? Betrachten wir uns im inneren der
deduce
Funktion, und wir sind nun schon vergangen, ein lvalue. Dies bedeutetT
ist einA&
und so das Ziel-Typ für die statische cast istA& &&
oder nurA&
. Dax
ist schon einA&
wir nichts tun und sind Links mit einer lvalue Referenz.Wenn wir schon vergangen, ein rvalue,
T
istA
so dass der Ziel-Typ für die statische cast istA&&
. Der cast-Ergebnisse in eine rvalue-Ausdruck, die nicht mehr an eine lvalue-Referenz. Haben wir beibehalten der Wert der Kategorie des Parameters.Putting dies zusammen gibt uns "perfect forwarding":
Wenn
f
erhält ein lvalue,E
bekommt ein lvalue. Wennf
erhält ein rvalue,E
bekommt ein rvalue. Perfekt.Und wir wollen natürlich, dass, um loszuwerden, die hässliche.
static_cast<T&&>
ist kryptisch und seltsam zu denken; lassen Sie uns stattdessen machen Sie eine utility-Funktion, die aufgerufen wirdforward
die nicht die gleiche Sache:InformationsquelleAutor der Antwort GManNickG
Ich denke, eine konzeptionelle und code Implementierung von std::forward, kann die Diskussion. Das ist eine Folie von Scott Meyers Vortrag Ein Effektives C++11/14 Sampler
Funktion
move
im code iststd::move
. Es gibt ein (Arbeits -) Umsetzung für Sie früher in diesem Vortrag. Ich fand die tatsächliche Implementierung von std::forward in libstdc++in der Datei bewegen.h, aber es ist überhaupt nicht aufschlußreich.Aus der Sicht des Benutzers, die Bedeutung ist, dass
std::forward
ist eine bedingte Typumwandlung in eine rvalue. Es kann nützlich sein, wenn ich Schreibe, eine Funktion, die erwartet, dass entweder ein lvalue oder rvalue " ein parameter und es passieren will, um eine andere Funktion als rvalue nur, wenn es übergeben wurde, als einen rvalue. Wenn ich nicht wickeln Sie die parameter in std::forward, wäre es immer übergeben als eine normale Referenz.Sicher genug, es druckt
Den code basiert auf einem Beispiel aus dem schon erwähnten Vortrag. Folie 10, gegen 15:00 Uhr, von Anfang an.
InformationsquelleAutor der Antwort user7610
Wenn Sie eine benannte rvalue-Referenz in einem Ausdruck ist es eigentlich ein lvalue (denn Sie beziehen sich auf das Objekt mit Namen). Betrachten Sie das folgende Beispiel:
Nun, nennen wir
outer
wie diesewürden wir uns wie 17 und 29 an #2, da 17 und 29 sind integer-Literale und als solche rvalues. Aber da
t1
undt2
im Ausdruckinner(t1,t2);
sind lvalues, würden Sie Sie aufrufen #1 statt #2. Das ist, warum wir brauchen, um die Verweise zurück in Unbenannte Referenzen mitstd::forward
. Alsot1
imouter
ist immer ein lvalue-Ausdruck, währendforward<T1>(t1)
kann ein rvalue-Ausdruck je nachT1
. Das letztere ist nur ein lvalue-Ausdruck, wennT1
ist ein lvalue-Referenz. UndT1
ist nur abgeleitet werden, um ein lvalue Referenz falls das erste argument die zu den äußeren war ein lvalue-Ausdruck.InformationsquelleAutor der Antwort sellibitze
Wenn, nach dem instanziieren,
T1
ist der Typchar
undT2
ist eine Klasse, die Sie übergeben möchtent1
pro Kopie undt2
proconst
Referenz. Gut, es sei denninner()
nimmt Sie nicht pro-const
Referenz, das heißt, in dem Fall, dass Sie wollen, es auch tun.Versuchen Sie zu schreiben, eine Reihe von
outer()
Funktionen, die diese umsetzen ohne rvalue-Referenzen, die Aufzucht der richtige Weg, um der übergabe von Argumenten voninner()
's geben. Ich denke, du wirst etwas brauchen, 2^2 von Ihnen, ziemlich heftige template-meta-Sachen, das ableiten der Argumente, und eine Menge Zeit, um dieses Recht für alle Fälle.Und dann kommt jemand daher mit einer
inner()
nimmt Argumente pro Zeiger. Ich denke, das macht jetzt 3^2. (Oder 4^2. Hölle, ich kann nicht sein belästigt, um zu versuchen zu denken, obconst
Zeiger würde einen Unterschied machen.)Und dann stellen Sie sich vor Sie wollen, tun Sie dies für fünf Parameter. Oder sieben.
Nun wissen Sie, warum einige helle Köpfe kam mit "perfect forwarding": Es macht der compiler tun alles für Sie.
InformationsquelleAutor der Antwort sbi
Einem Punkt, der nicht gemacht worden, Kristall-klar ist, dass
static_cast<T&&>
Griffeconst T&
richtig zu.Programm:
Produziert:
Beachten Sie, dass 'f' hat eine template-Funktion. Wenn es nur definiert als 'void f(int&& a)' das funktioniert nicht.
InformationsquelleAutor der Antwort lalawawa
Kann es sinnvoll sein, zu betonen, dass vor hat zu sein verwendet im tandem mit einer äußeren Methode mit Speditions - /universal-Bezug. Mit Hilfe der vorwärts-von sich selbst-der folgenden Aussagen ist erlaubt, aber nicht gut tut, anderen als Verwirrung zu Stiften. Die standard-Ausschuss kann wollen, deaktivieren Sie diese Flexibilität ist ansonsten, warum wir nicht einfach static_cast verwenden statt?
Meiner Meinung nach, verschieben und weiterleiten design-Muster, die Natürliche Ergebnisse nach der r-Wert-Referenz-Typ eingeführt wird. Wir sollten Sie nicht den Namen einer Methode, vorausgesetzt, es wird richtig verwendet werden, wenn eine nicht bestimmungsgemäße Verwendung ist verboten.
InformationsquelleAutor der Antwort colin