C ++ 20 wird eingeführt std::common_reference
. Was ist seine Aufgabe? Kann jemand ein Beispiel dafür geben?
C ++ 20 wird eingeführt std::common_reference
. Was ist seine Aufgabe? Kann jemand ein Beispiel dafür geben?
Antworten:
common_reference
kam aus meinen Bemühungen heraus, eine Konzeptualisierung der STL-Iteratoren zu entwickeln, die Proxy-Iteratoren unterstützt.
In der STL haben Iteratoren zwei Arten von besonderem Interesse: reference
und value_type
. Ersteres ist der Rückgabetyp des Iterators operator*
und der Typ value_type
(nicht konstant, nicht referenziert) der Elemente der Sequenz.
Generische Algorithmen müssen häufig Folgendes tun:
value_type tmp = *it;
... so dass wir wissen , dass es müssen einige Beziehung zwischen diesen beiden Typen. Für Nicht-Proxy-Iteratoren ist die Beziehung einfach: Sie reference
ist immer value_type
optional optional und referenzqualifiziert. Frühe Versuche, das InputIterator
Konzept zu definieren, erforderten, dass der Ausdruck *it
konvertierbar war const value_type &
, und für die interessantesten Iteratoren ist dies ausreichend.
Ich wollte, dass Iteratoren in C ++ 20 leistungsfähiger sind. Betrachten Sie beispielsweise die Anforderungen von a zip_iterator
, die zwei Sequenzen im Sperrschritt durchlaufen. Wenn Sie a dereferenzieren zip_iterator
, erhalten Sie einen temporären pair
Typ der beiden Iteratoren reference
. Also, zip
‚ing a vector<int>
und vector<double>
hätte diesen im Zusammenhang Typen:
zip
Iterator reference
: pair<int &, double &>
zip
Iterator value_type
:pair<int, double>
Wie Sie sehen können, sind diese beiden Typen nicht einfach durch Hinzufügen einer Lebenslauf- und Referenzqualifikation auf höchster Ebene miteinander verbunden. Und doch fühlt es sich falsch an, die beiden Typen willkürlich unterschiedlich sein zu lassen. Hier besteht eindeutig eine Beziehung. Aber wie ist die Beziehung und was können generische Algorithmen, die mit Iteratoren arbeiten, sicher über die beiden Typen annehmen?
Die Antwort in C ++ 20 lautet, dass für jeden gültigen Iteratortyp, ob Proxy oder nicht, die Typen reference &&
und value_type &
eine gemeinsame Referenz verwendet werden . Mit anderen Worten, für einige Iteratoren it
gibt es einen Typ CR
, der Folgendes gut formuliert :
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CR
ist die gemeinsame Referenz. Alle Algorithmen können sich auf die Tatsache verlassen, dass dieser Typ existiert, und können std::common_reference
zur Berechnung verwendet werden.
Das ist also die Rolle, common_reference
die in der STL in C ++ 20 spielt. Im Allgemeinen können Sie diese ignorieren, es sei denn, Sie schreiben generische Algorithmen oder Proxy-Iteratoren. Es ist unter der Decke vorhanden, um sicherzustellen, dass Ihre Iteratoren ihren vertraglichen Verpflichtungen nachkommen.
EDIT: Das OP hat auch nach einem Beispiel gefragt. Dies ist ein wenig erfunden, aber stellen Sie sich vor, es ist C ++ 20, und Sie erhalten einen Typbereich mit wahlfreiem Zugriff, r
von R
dem Sie nichts wissen und den Sie für sort
den Bereich benötigen.
Stellen Sie sich weiter vor, dass Sie aus irgendeinem Grund eine monomorphe Vergleichsfunktion verwenden möchten, wie z std::less<T>
. (Vielleicht haben Sie den Bereich getippt, und Sie müssen auch die Vergleichsfunktion tippen und durch ein virtual
? Wieder eine Strecke führen.) Was sollte drin T
sein std::less<T>
? Dafür würden Sie verwenden common_reference
, oder den Helfer, iter_common_reference_t
der in Bezug darauf implementiert ist.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Dies funktioniert garantiert auch dann, wenn der Bereich r
über Proxy-Iteratoren verfügt.
pair<T&,U&>
und pair<T,U>&
hätte eine gemeinsame Referenz, und es wäre einfach pair<T&,U&>
. Es std::pair
gibt jedoch keine Konvertierung von pair<T,U>&
nach pair<T&,U&>
, obwohl eine solche Konvertierung im Prinzip sinnvoll ist. (Dies ist übrigens der Grund, warum wir zip
in C ++ 20 keine Ansicht haben .)
pair
anstelle eines Typs verwendet werden müsste , der speziell für diesen Zweck entwickelt werden könnte mit geeigneten impliziten Konvertierungen nach Bedarf?
std::pair
; Jeder geeignete paarartige Typ mit den entsprechenden Konvertierungen reicht aus, und range-v3 definiert einen solchen paarartigen Typ. Im Ausschuss gefiel es der LEWG nicht, der Standardbibliothek einen Typ hinzuzufügen, der fast, aber nicht ganz std::pair
, ob normativ oder nicht, war, ohne zuvor die Vor- und Nachteile einer einfachen std::pair
Arbeit sorgfältig zu prüfen .
tuple
, pair
, tomato
, to
- MAH
- to
. pair
hat diese nette Funktion, mit der Sie auf die Elemente mit .first
und zugreifen können .second
. Strukturierte Bindungen helfen bei der Unbeholfenheit, mit tuple
s zu arbeiten, aber nicht bei allen.