Warum muss es sein std::reference_wrapper
? Wo soll es verwendet werden? Wie unterscheidet es sich von einem einfachen Zeiger? Wie ist seine Leistung im Vergleich zu einem einfachen Zeiger?
99
Warum muss es sein std::reference_wrapper
? Wo soll es verwendet werden? Wie unterscheidet es sich von einem einfachen Zeiger? Wie ist seine Leistung im Vergleich zu einem einfachen Zeiger?
.
mit anstelle von->
.
funktioniert nicht so, wie Sie es vorschlagen (es sei denn, irgendwann wird der Vorschlag für den Operatorpunkt angenommen und integriert :))get()
Member-Funktion oder mit seiner impliziten Konvertierung zurück in den zugrunde liegenden Typ verwendet.Antworten:
std::reference_wrapper
ist nützlich in Kombination mit Vorlagen. Es umschließt ein Objekt, indem es einen Zeiger darauf speichert, wodurch eine Neuzuweisung und ein Kopieren ermöglicht werden, während die übliche Semantik nachgeahmt wird. Außerdem werden bestimmte Bibliotheksvorlagen angewiesen, Referenzen anstelle von Objekten zu speichern.Betrachten Sie die Algorithmen in der STL, die Funktoren kopieren: Sie können diese Kopie vermeiden, indem Sie einfach einen Referenz-Wrapper übergeben, der sich auf den Funktor anstelle des Funktors selbst bezieht:
Das funktioniert, weil…
…
reference_wrapper
S Überlastung,operator()
so dass sie genau wie die Funktionsobjekte aufgerufen werden können, auf die sie sich beziehen:… (Un) wie bei normalen Referenzen wird beim Kopieren (und Zuweisen)
reference_wrappers
nur der Pointee zugewiesen.Das Kopieren eines Referenz-Wrappers entspricht praktisch dem Kopieren eines Zeigers, der so billig wie möglich ist. Alle Funktionsaufrufe, die mit der Verwendung verbunden sind (z. B. die zu
operator()
), sollten nur als Inline-Aufrufe eingefügt werden.reference_wrapper
s werden erstellt überstd::ref
undstd::cref
:Das Template-Argument gibt den Typ und die Lebenslaufqualifikation des Objekts an, auf das verwiesen wird.
r2
bezieht sich auf aconst int
und gibt nur einen Verweis aufconst int
. Aufrufe von Referenz-Wrappern mitconst
Funktoren rufen nurconst
Mitgliedsfunktionen aufoperator()
.R-Wert-Initialisierer sind nicht zulässig, da ihre Zulassung mehr schaden als nützen würde. Da r-Werte ohnehin verschoben würden (und bei garantierter Kopierentfernung auch dies teilweise vermieden wird), verbessern wir die Semantik nicht. Wir können jedoch baumelnde Zeiger einführen, da ein Referenz-Wrapper die Lebensdauer des Pointees nicht verlängert.
Bibliotheksinteraktion
Wie bereits erwähnt, kann man anweisen
make_tuple
, eine Referenz im Ergebnis zu speichern,tuple
indem man das entsprechende Argument durch areference_wrapper
:Beachten Sie, dass sich dies geringfügig unterscheidet von
forward_as_tuple
: Hier sind rWerte als Argumente nicht zulässig.std::bind
zeigt das gleiche Verhalten: Es wird das Argument nicht kopiert, sondern eine Referenz gespeichert, wenn es eine istreference_wrapper
. Nützlich, wenn dieses Argument (oder der Funktor!) Nicht kopiert werden muss, sondern im Gültigkeitsbereich bleibt, während derbind
Funktor verwendet wird.Unterschied zu gewöhnlichen Zeigern
Es gibt keine zusätzliche Ebene der syntaktischen Indirektion. Zeiger müssen dereferenziert werden, um einen Wert für das Objekt zu erhalten, auf das sie sich beziehen.
reference_wrapper
s haben einen impliziten Konvertierungsoperator und können wie das Objekt aufgerufen werden, das sie umschließen.reference_wrapper
s haben im Gegensatz zu Zeigern keinen Nullstatus. Sie müssen entweder mit einer Referenz oder einer anderenreference_wrapper
initialisiert werden .Eine Ähnlichkeit ist die flache Kopiersemantik: Zeiger und
reference_wrapper
s können neu zugewiesen werden.quelle
std::make_tuple(std::ref(i));
überlegenstd::make_tuple(&i);
?i
, keinen Verweis darauf.Es gibt mindestens zwei motivierende Zwecke
std::reference_wrapper<T>
:Es dient dazu, Objekten, die als Wertparameter an Funktionsvorlagen übergeben werden, eine Referenzsemantik zu geben. Beispielsweise möchten Sie möglicherweise ein großes Funktionsobjekt übergeben, an
std::for_each()
das der Funktionsobjektparameter als Wert übergeben wird. Um das Kopieren des Objekts zu vermeiden, können Sie verwendenDas Übergeben von Argumenten
std::reference_wrapper<T>
für einenstd::bind()
Ausdruck ist weit verbreitet, um Argumente eher nach Referenz als nach Wert zu binden.Bei Verwendung eines
std::reference_wrapper<T>
mitstd::make_tuple()
dem entsprechenden Tupelelement wirdT&
eher ein als einT
:quelle
fun
handelt es sich um ein Funktionsobjekt (dh ein Objekt einer Klasse mit einem Funktionsaufrufoperator) und nicht um eine Funktion: Wennfun
es sich um eine tatsächliche Funktion handelt,std::ref(fun)
haben Sie keinen Zweck und machen den Code möglicherweise langsamer.Ein weiterer Unterschied in Bezug auf
reference_wrapper
selbstdokumentierenden Code besteht darin, dass die Verwendung eines Codes das Eigentum an dem Objekt im Wesentlichen ablehnt. Im Gegensatz dazuunique_ptr
behauptet ein Ass den Besitz, während ein bloßer Zeiger möglicherweise im Besitz ist oder nicht (es ist nicht möglich zu wissen, ohne viele verwandte Codes zu betrachten):quelle
reference_wrapper
ist rohen Zeigern nicht nur überlegen, weil klar ist, dass es nicht im Besitz ist, sondern auch, weil es nicht sein kannnullptr
(ohne Spielereien) und Benutzer wissen, dass sie nicht bestehen könnennullptr
(ohne Spielereien) und Sie wissen, dass Sie es nicht müssen überprüfe es.Sie können es sich als praktischen Wrapper für Referenzen vorstellen, damit Sie sie in Containern verwenden können.
Es ist im Grunde eine
CopyAssignable
Version vonT&
. Jedes Mal, wenn Sie eine Referenz wünschen, diese aber zuweisbar sein, verwendenstd::reference_wrapper<T>
oder ihre Hilfsfunktion haben mussstd::ref()
. Oder verwenden Sie einen Zeiger.Andere Macken:
sizeof
:Und Vergleich:
quelle
reference_wrapper
Code einbindet und ihn mit Code identisch macht, der einen Zeiger oder eine Referenz verwendet.std::reference_wrapper
hat die Garantie, dass das Objekt niemals null ist. Betrachten Sie ein Klassenmitgliedstd::vector<T *>
. Sie müssen den gesamten Klassencode untersuchen, um festzustellen, ob dieses Objekt jemals einnullptr
im Vektor speichern kann , währendstd::reference_wrapper<T>
Sie mit garantiert gültige Objekte haben.