C++20 führt ein std::common_reference
. Was ist seine Aufgabe? Kann jemand ein Beispiel für die Verwendung geben?
Was ist der Zweck von C++20 std::common_reference?
康桓瑋
Erich Niebler
common_reference
ist aus meinen Bemühungen hervorgegangen, eine Konzeptualisierung der Iteratoren von STL zu entwickeln, die Proxy-Iteratoren berücksichtigt.
In der STL haben Iteratoren zwei zugehörige Typen von besonderem Interesse: reference
und value_type
. Ersteres ist der Rückgabetyp des Iterators operator*
und der value_type
ist der (nicht konstante, nicht referenzierende) Typ der Elemente der Sequenz.
Generische Algorithmen müssen oft Dinge wie die folgenden tun:
value_type tmp = *it;
… also wissen wir, dass es das geben muss etwas Beziehung zwischen diesen beiden Typen. Für Nicht-Proxy-Iteratoren ist die Beziehung einfach: reference
ist immer value_type
optional const und referenzqualifiziert. Frühe Definitionsversuche InputIterator
Konzept erforderlich, dass der Ausdruck *it
war umwandelbar const value_type &
und für die meisten interessanten Iteratoren ist das ausreichend.
Ich wollte, dass Iteratoren in C++20 leistungsfähiger sind. Betrachten Sie zum Beispiel die Bedürfnisse von a zip_iterator
die zwei Sequenzen im Lock-Step iteriert. Wenn Sie a zip_iterator
erhalten Sie eine temporäre pair
der beiden Iteratoren reference
Typen. So, zip
bin ein vector<int>
und ein vector<double>
hätte diese zugeordneten 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 das Hinzufügen von Lebenslauf- und Referenzqualifikationen auf höchster Ebene miteinander verwandt. Und doch fühlt es sich falsch an, die beiden Typen willkürlich unterschiedlich sein zu lassen. Ganz klar gibt es das etwas Beziehung hier. Aber was 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 for irgendein gültiger Iteratortyp, Proxy oder nicht, die Typen reference &&
und value_type &
Teile ein gemeinsame Referenz. Mit anderen Worten, für einen Iterator it
es gibt irgendeinen typ CR
was folgendes wohlgeformt macht:
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 darauf verlassen, dass dieser Typ existiert, und verwenden können std::common_reference
um es zu berechnen.
Das ist also die Rolle, die common_reference
spielt in der STL in C++20. Im Allgemeinen können Sie es ignorieren, es sei denn, Sie schreiben generische Algorithmen oder Proxy-Iteratoren. Es befindet sich unter der Decke und stellt sicher, dass Ihre Iteratoren ihre vertraglichen Verpflichtungen erfüllen.
BEARBEITEN: 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 Bereich mit wahlfreiem Zugriff r
des Typs R
von denen du nichts weißt, und du willst sort
die Reichweite.
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 gelöscht, und Sie müssen auch die Vergleichsfunktion löschen und durch a virtual
? Wieder eine Strecke.) Was sollte T
drin sein std::less<T>
? Dafür würden Sie verwenden common_reference
oder der Helfer iter_common_reference_t
was darin umgesetzt wird.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Das geht garantiert, auch wenn Reichweite r
hat Proxy-Iteratoren.
-
Vielleicht bin ich dicht, aber können Sie klarstellen, was die gemeinsame Referenz im Zip-Pair-Beispiel ist?
– happydave
25. November 2019 um 1:58 Uhr
-
Im Idealfall,
pair<T&,U&>
undpair<T,U>&
hätte eine gemeinsame Referenz, und es wäre einfachpair<T&,U&>
. Allerdings zstd::pair
es gibt keine Konvertierung vonpair<T,U>&
zupair<T&,U&>
auch wenn eine solche Umstellung grundsätzlich sinnvoll ist. (Deshalb haben wir übrigens auch keinezip
Ansicht in C++20.)– Eric Niebler
25. November 2019 um 2:03 Uhr
-
@EricNiebler: “Das ist übrigens der Grund, warum wir in C++20 keine Zip-Ansicht haben.” Gibt es einen Grund, warum ein Zip-Iterator verwenden müsste
pair
anstelle eines Typs, der speziell für seinen Zweck entworfen werden könnte, mit geeigneten impliziten Konvertierungen nach Bedarf?– Nicol Bolas
25. November 2019 um 2:44 Uhr
-
@Nicol Bolas Es besteht keine Notwendigkeit zu verwenden
std::pair
; jeder geeignete paarartige Typ mit den entsprechenden Konvertierungen reicht aus, und range-v3 definiert einen solchen paarartigen Typ. Im Komitee mochte LEWG die Idee nicht, der Standardbibliothek einen Typ hinzuzufügen, der fast, aber nicht ganz warstd::pair
sei es normativ oder nicht, ohne vorher die Vor- und Nachteile des einfachen Herstellens gebührend zu prüfenstd::pair
Arbeit.– Eric Niebler
25. November 2019 um 19:16 Uhr
-
tuple
,pair
,tomato
,to
–MAH
–to
.pair
hat diese nette Funktion, mit der Sie auf die Elemente zugreifen können.first
und.second
. Strukturierte Bindungen helfen bei der Umständlichkeit der Arbeit mittuple
s, aber nicht alle.– Eric Niebler
28. November 2019 um 21:57 Uhr