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:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
Das funktioniert, weil…
… reference_wrapper
S Überlastung,operator()
so dass sie genau wie die Funktionsobjekte aufgerufen werden können, auf die sie sich beziehen:
std::ref(myEngine)() // Valid expression, modifies myEngines state
… (Un) wie bei normalen Referenzen wird beim Kopieren (und Zuweisen) reference_wrappers
nur der Pointee zugewiesen.
int i, j;
auto r = std::ref(i); // r refers to i
r = std::ref(j); // Okay; r refers to j
r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
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 über std::ref
undstd::cref
:
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>
Das Template-Argument gibt den Typ und die Lebenslaufqualifikation des Objekts an, auf das verwiesen wird. r2
bezieht sich auf a const int
und gibt nur einen Verweis auf const int
. Aufrufe von Referenz-Wrappern mit const
Funktoren rufen nur const
Mitgliedsfunktionen auf operator()
.
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 a reference_wrapper
:
int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>
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 ist reference_wrapper
. Nützlich, wenn dieses Argument (oder der Funktor!) Nicht kopiert werden muss, sondern im Gültigkeitsbereich bleibt, während der bind
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.
int i;
int& ref = std::ref(i); // Okay
reference_wrapper
s haben im Gegensatz zu Zeigern keinen Nullstatus. Sie müssen entweder mit einer Referenz oder einer anderenreference_wrapper
initialisiert werden .
std::reference_wrapper<int> r; // Invalid
Eine Ähnlichkeit ist die flache Kopiersemantik: Zeiger und reference_wrapper
s können neu zugewiesen werden.
.
mit anstelle von->