Was ist der Zweck von C++20 std::common_reference?

Lesezeit: 4 Minuten

Benutzer-Avatar
康桓瑋

C++20 führt ein std::common_reference. Was ist seine Aufgabe? Kann jemand ein Beispiel für die Verwendung geben?

Benutzer-Avatar
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_typeoptional 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_iteratorerhalten Sie eine temporäre pair der beiden Iteratoren reference Typen. So, zipbin 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_referenceoder 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&> und pair<T,U>& hätte eine gemeinsame Referenz, und es wäre einfach pair<T&,U&>. Allerdings z std::paires gibt keine Konvertierung von pair<T,U>& zu pair<T&,U&> auch wenn eine solche Umstellung grundsätzlich sinnvoll ist. (Deshalb haben wir übrigens auch keine zip 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 pairanstelle 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 war std::pairsei es normativ oder nicht, ohne vorher die Vor- und Nachteile des einfachen Herstellens gebührend zu prüfen std::pair Arbeit.

    – Eric Niebler

    25. November 2019 um 19:16 Uhr

  • tuple, pair, tomato, toMAHto. 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 mit tuples, aber nicht alle.

    – Eric Niebler

    28. November 2019 um 21:57 Uhr

1012260cookie-checkWas ist der Zweck von C++20 std::common_reference?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy