Was ist eine „Span“ und wann sollte ich eine verwenden?

Lesezeit: 12 Minuten

Was ist eine „Span und wann sollte ich eine verwenden
einpoklum

Kürzlich habe ich Vorschläge zur Verwendung bekommen span<T>‘s in meinem Code, oder habe hier auf der Website einige Antworten gesehen, die verwenden span‘s – angeblich eine Art Container. Aber – ich kann so etwas in der C++17-Standardbibliothek nicht finden.

Also, was ist das mysteriöse span<T>und warum (oder wann) ist es eine gute Idee, es zu verwenden, wenn es kein Standard ist?

  • std::span wurde 2017 vorgeschlagen. Es gilt für C++17 oder C++20. Siehe auch P0122R5, span: Grenzensichere Ansichten für Sequenzen von Objekten. Möchten Sie diese Sprache wirklich ansprechen? Es wird Jahre dauern, bis die Compiler aufholen.

    – jww

    24. Januar 2018 um 0:29 Uhr

  • @jww: Spans sind mit C ++ 11 durchaus verwendbar … als gsl::span eher, als std::span. Siehe auch meine Antwort unten.

    – einpoklum

    24. Januar 2018 um 6:39 Uhr

  • Auch dokumentiert auf cpreference.com: de.cppreference.com/w/cpp/container/span

    – Keith Thompson

    24. März 2020 um 19:18 Uhr

  • @KeithThompson: Nicht im Jahr 2017 war es nicht …

    – einpoklum

    24. März 2020 um 21:02 Uhr

  • @jww Alle Compiler unterstützen std::span<> jetzt im C++20-Modus. Und span ist in vielen Bibliotheken von Drittanbietern verfügbar. Sie hatten recht – es waren Jahre: 2 Jahre um genau zu sein.

    – Contango

    20. Juni 2020 um 7:43 Uhr


Was ist eine „Span und wann sollte ich eine verwenden
einpoklum

Was ist es?

EIN span<T> ist:

  • Eine sehr leichte Abstraktion einer zusammenhängenden Folge von Werten des Typs T irgendwo in Erinnerung.
  • Grundsätzlich a struct { T * ptr; std::size_t length; } mit einer Reihe von Convenience-Methoden.
  • Ein nicht besitzender Typ (dh ein „Referenztyp“ anstelle eines „Werttyps“): Er weist niemals etwas zu oder hebt die Zuweisung auf und hält keine intelligenten Zeiger am Leben.

Früher hieß es ein array_view und noch früher als array_ref.

Wann sollte ich es verwenden?

Erstens wann nicht um es zu benutzen:

  • Verwenden Sie es nicht in Code, der nur ein beliebiges Paar von Start- und End-Iteratoren verwenden könnte, wie z std::sort, std::find_if, std::copy und all diese supergenerischen Vorlagenfunktionen.
  • Verwenden Sie es nicht, wenn Sie einen Standardbibliothekscontainer (oder einen Boost-Container usw.) haben, von dem Sie wissen, dass er für Ihren Code geeignet ist. Es ist nicht beabsichtigt, einen von ihnen zu ersetzen.

Nun zur tatsächlichen Verwendung:

Verwenden span<T> (bzw, span<const T>) statt einer freistehenden T* (bzw const T*), wenn auch die zugewiesene Länge oder Größe eine Rolle spielt. Ersetzen Sie also Funktionen wie:

void read_into(int* buffer, size_t buffer_size);

mit:

void read_into(span<int> buffer);

Warum sollte ich es verwenden? Warum ist es gut?

Oh, Spannweiten sind genial! Verwendung einer span

  • bedeutet, dass Sie mit dieser Kombination aus Zeiger + Länge / Start + Ende-Zeiger wie mit einem schicken, aufgemotzten Standard-Bibliothekscontainer arbeiten können, z.

    • for (auto& x : my_span) { /* do stuff */ }
    • std::find_if(my_span.cbegin(), my_span.cend(), some_predicate);
    • std::ranges::find_if(my_span, some_predicate); (in C++20)

    … aber absolut ohne den Overhead, der für die meisten Containerklassen anfällt.

  • lässt den Compiler manchmal mehr Arbeit für Sie erledigen. Zum Beispiel dies:

    int buffer[BUFFER_SIZE];
    read_into(buffer, BUFFER_SIZE);
    

    wird das:

    int buffer[BUFFER_SIZE];
    read_into(buffer);
    

    … die das tut, was Sie wollen. Siehe auch Richtlinie P.5.

  • ist die sinnvolle Alternative zum Bestehen const vector<T>& to funktioniert, wenn Sie erwarten, dass Ihre Daten zusammenhängend im Speicher sind. Lassen Sie sich nicht mehr von hochrangigen C++-Gurus schimpfen!

  • erleichtert die statische Analyse, sodass der Compiler Ihnen möglicherweise helfen kann, dumme Fehler zu finden.

  • ermöglicht die Debug-Kompilierungs-Instrumentierung zur Überprüfung der Laufzeitgrenzen (dh spanDie Methoden von enthalten einige Grenzen überprüfenden Code #ifndef NDEBUG#endif)

  • zeigt an, dass Ihr Code (der die Spanne verwendet) den Speicher, auf den verwiesen wird, nicht besitzt.

Es gibt noch mehr Motivation für die Verwendung spans, die Sie in der finden könnten C++ Kernrichtlinien – Aber du verstehst den Drift.

Aber ist es in der Standardbibliothek?

bearbeiten: Jawohl, std::span wurde mit der C++20-Version der Sprache zu C++ hinzugefügt!

Warum nur in C++20? Nun, die Idee ist zwar nicht neu – ihre jetzige Form wurde aber in Verbindung mit der konzipiert C++ Kernrichtlinien Projekt, das erst 2015 Gestalt annahm. Es hat also eine Weile gedauert.

Wie verwende ich es also, wenn ich C++17 oder früher schreibe?

Es ist Teil der Kernrichtlinien‘s Support Library (GSL). Implementierungen:

  • Microsoft / Neil Macintosh GSL enthält eine eigenständige Implementierung: gsl/span
  • GSL-Lite ist eine Single-Header-Implementierung der gesamten GSL (es ist nicht so groß, keine Sorge), einschließlich span<T>.

Die GSL-Implementierung geht im Allgemeinen von einer Plattform aus, die C++14-Unterstützung implementiert [11]. Diese alternativen Single-Header-Implementierungen sind nicht von GSL-Einrichtungen abhängig:

Beachten Sie, dass diese verschiedenen Span-Implementierungen einige Unterschiede in den Methoden/Unterstützungsfunktionen aufweisen, mit denen sie ausgestattet sind; und sie können auch etwas von der Version abweichen, die in C++20 in die Standardbibliothek übernommen wurde.


Weiterlesen: Alle Details und Designüberlegungen finden Sie im endgültigen offiziellen Vorschlag vor C++17, P0122R7: span: Grenzensichere Ansichten für Sequenzen von Objekten von Neal Macintosh und Stephan J. Lavavej. Es ist aber ein bisschen lang. Außerdem hat sich in C++20 die Span-Vergleichssemantik geändert (im Folgenden dieses kurze Papier von Tony van Eerd).

  • Es wäre sinnvoller, einen allgemeinen Bereich zu standardisieren (unterstützt Iterator+Sentinel und Iterator+Länge, vielleicht sogar Iterator+Sentinel+Länge) und span zu einer einfachen Typdefinition zu machen. Denn das ist allgemeiner.

    – Deduplizierer

    17. August 2017 um 12:47 Uhr

  • @Deduplicator: Ranges kommen zu C++, aber der aktuelle Vorschlag (von Eric Niebler) erfordert Unterstützung für Concepts. Also nicht vor C++20.

    – einpoklum

    17. August 2017 um 12:52 Uhr

  • @HảiPhạmLê: Arrays zerfallen nicht sofort in Zeiger. versuchen zu tun std::cout << sizeof(buffer) << '\n' und Sie werden sehen, dass Sie 100 sizeof(int)’s erhalten.

    – einpoklum

    23. August 2017 um 7:59 Uhr

  • @ Jim std::array ein Container ist, besitzt er die Werte. span ist nicht besitzend

    – Caleth

    4. Dezember 2018 um 14:18 Uhr

  • @ Jim: std::array ist ein ganz anderes Biest. Seine Länge wird zur Kompilierzeit festgelegt und es ist eher ein Werttyp als ein Referenztyp, wie Caleth erklärt hat.

    – einpoklum

    4. Dezember 2018 um 14:56 Uhr

Was ist eine „Span und wann sollte ich eine verwenden
Gabriel Staples

EIN span<T> ist das:

template <typename T>
struct span
{
    T * ptr_to_array;   // pointer to a contiguous C-style array of data
                        // (which memory is NOT allocated or deallocated 
                        // by the span)
    std::size_t length; // number of elements of type `T` in the array

    // Plus a bunch of constructors and convenience accessor methods here
}

Es ist ein leichtgewichtiger Wrapper um ein Array im C-Stil, der von C++-Entwicklern bevorzugt wird, wenn sie C-Bibliotheken verwenden und sie mit einem Datencontainer im C++-Stil für „Typsicherheit“ und „C++-Charakter“ und „Feelgoodery“ umschließen möchten “. 🙂

Hinweis: Ich nenne den oben definierten Struct-Container, der als Span bekannt ist, einen „leichten Wrapper um ein Array im C-Stil“, weil er auf einen zusammenhängenden Teil des Speichers zeigt, z. B. ein Array im C-Stil, und ihn umschließt Zugriffsmethoden und die Größe des Arrays. Das meine ich mit “leichtem Wrapper”: Es ist ein Wrapper um einen Zeiger und eine Längenvariable plus Funktionen.


Weitergehen:

@einpoklum macht einen ziemlich guten Job bei der Einführung, was a span ist in seiner Antwort hier. Aber, Auch nachdem ich seine Antwort gelesen habe, Es ist leicht für jemanden, der neu in Spans ist, immer noch eine Reihe von Gedankenstrom-Fragen zu haben, die nicht vollständig beantwortet sind, wie zum Beispiel die folgenden:

  1. Wie ist ein span anders als ein C-Array? Warum nicht einfach so einen verwenden? Es scheint, als wäre es nur einer von denen mit der bekannten Größe …
  2. Warte, das klingt wie ein std::arraywie ist ein span anders als das?
  3. Oh, das erinnert mich, ist kein std::vector wie ein std::array zu?
  4. Ich bin so verwirrt. 🙁 Was ist ein span?

Also, hier ist etwas zusätzliche Klarheit dazu:

DIREKTES ZITAT SEINER ANTWORT – MIT MEINE ERGÄNZUNGEN und Kommentare in Klammern IN FETT und meine Hervorhebung in Kursivschrift:

Was ist es?

EIN span<T> ist:

  • Eine sehr leichte Abstraktion von a zusammenhängende Folge von Werten des Typs T irgendwo in Erinnerung.
  • Grundsätzlich a Einzel Struktur { T * ptr; std::size_t length; } mit einer Reihe von Convenience-Methoden. (Beachten Sie, dass dies deutlich anders ist als std::array<> weil ein span ermöglicht komfortable Accessor-Methoden, vergleichbar mit std::arrayüber ein Zeiger zum Typ T und Länge (Anzahl der Elemente) des Typs Twohingegen std::array ist ein tatsächlicher Behälter, der einen oder mehrere enthält Werte des Typs T.)
  • EIN nicht besitzender Typ (dh eher ein “Referenztyp” als ein “Werttyp”): It Weist niemals etwas zu oder hebt die Zuweisung auf und hält Smart Pointer nicht am Leben.

Früher hieß es ein array_view und noch früher als array_ref.

Diese fettgedruckten Teile sind kritisch Verstehen Sie sie also nicht, verpassen Sie sie nicht und lesen Sie sie nicht falsch! EIN span ist KEIN C-Array von Strukturen, noch ist es eine Struktur eines C-Arrays vom Typ T plus die Länge des Arrays (dies wäre im Wesentlichen das, was die std::array Container ist), NOCH ist es ein C-Array von Strukturen von Zeigern auf den Typ T plus die Länge, sondern es ist ein Einzel Struktur, die eine einzige enthält Zeiger zum Typ Tund das Längedas ist die Anzahl der Elemente (vom Typ T) in dem zusammenhängenden Speicherblock, auf den der Zeiger tippen soll T verweist auf! Auf diese Weise wird der einzige Overhead, den Sie durch die Verwendung von a hinzugefügt haben span sind die Variablen, um den Zeiger und die Länge zu speichern, sowie alle praktischen Zugriffsfunktionen, die Sie verwenden span bietet.

Dies ist im Gegensatz zu a std::array<> weil die std::array<> weist tatsächlich Speicher für den gesamten zusammenhängenden Block zu und ist UNLIKE std::vector<> weil ein std::vector ist im Grunde nur ein std::array das geht auch dynamisch wachsend (normalerweise verdoppelt sich die Größe) jedes Mal, wenn es sich füllt und Sie versuchen, etwas anderes hinzuzufügen. EIN std::array in der Größe festgelegt ist, und ein span verwaltet nicht einmal den Speicher des Blocks, auf den es zeigt, es zeigt nur auf den Speicherblock, weiß, wie lang der Speicherblock ist, weiß, welcher Datentyp sich in einem C-Array im Speicher befindet, und stellt einen bequemen Zugriff bereit Funktionen, um mit den Elementen in diesem zusammenhängenden Speicher zu arbeiten.

Es ist Teil des C++-Standards:

std::span ist ab C++20 Teil des C++-Standards. Sie können seine Dokumentation hier lesen: https://en.cppreference.com/w/cpp/container/span. Um zu sehen, wie man Google verwendet absl::Span<T>(array, length) in C++11 oder höher heutesiehe unten.

Zusammenfassende Beschreibungen und Schlüsselreferenzen:

  1. std::span<T, Extent> (Extent = “die Anzahl der Elemente in der Folge, oder std::dynamic_extent wenn dynamisch”. Eine Spanne nur verweist auf Speicher und erleichtert den Zugriff, verwaltet ihn aber NICHT!):
  2. https://en.cppreference.com/w/cpp/container/span
  3. std::array<T, N> (Beachten Sie, dass es eine Fest Größe N!):
  4. https://en.cppreference.com/w/cpp/container/array
  5. http://www.cplusplus.com/reference/array/array/
  6. std::vector<T> (wird bei Bedarf automatisch dynamisch vergrößert):
  7. https://en.cppreference.com/w/cpp/container/vector
  8. http://www.cplusplus.com/reference/vector/vector/

Wie kann ich verwenden span in C++11 oder höher heute?

Google hat seine internen C++11-Bibliotheken in Form seiner „Abseil“-Bibliothek als Open Source bereitgestellt. Diese Bibliothek soll Funktionen von C++14 bis C++20 und darüber hinaus bereitstellen, die in C++11 und höher funktionieren, damit Sie die Funktionen von morgen schon heute nutzen können. Sie sagen:

Kompatibilität mit dem C++-Standard

Google hat viele Abstraktionen entwickelt, die entweder mit den in C++14, C++17 und darüber hinaus integrierten Funktionen übereinstimmen oder genau übereinstimmen. Durch die Verwendung der Abseil-Versionen dieser Abstraktionen können Sie jetzt auf diese Funktionen zugreifen, selbst wenn Ihr Code noch nicht bereit für das Leben in einer Post-C++11-Welt ist.

Hier sind einige wichtige Ressourcen und Links:

  1. Hauptseite: https://abseil.io/
  2. https://abseil.io/docs/cpp/
  3. GitHub-Repository: https://github.com/abseil/abseil-cpp
  4. span.h Kopfzeile und absl::Span<T>(array, length) Vorlagenklasse: https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L153

Andere Referenzen:

  1. Struct mit Template-Variablen in C++
  2. Wikipedia: C++-Klassen
  3. Standardsichtbarkeit von C++-Klassen-/Strukturmitgliedern

Verwandt:

  1. [another one of my answers on templates and spans] Wie man Span of Spans macht

  • Ich würde wirklich nicht empfehlen, das gesamte Abseilen zu verwenden, um einen Span-Kurs zu erhalten.

    – einpoklum

    28. August 2020 um 10:19 Uhr

  • Ich habs. Der größte Vorteil ist das geringe Gewicht.

    – Yushang

    1. Juli 2021 um 12:33 Uhr

  • @yushang, aus Sicht eines C++-Entwicklers denke ich, dass der größte Vorteil nicht “leichtgewichtig” ist, sondern eher: “umschließt bereits vorhandene C-Arrays”, sodass kein Kopieren erforderlich ist, und Sie haben jetzt einen Wrapper um einen Container, der seine Größe enthält, im Gegensatz zu C-Arrays, die keine Informationen über ihre eigene Größe kennen oder mit sich herumtragen. Als Embedded-Entwickler mit mehr Erfahrung in C als in C++ würde ich es jedoch im Allgemeinen vorziehen, über einen Zeitraum hinweg sowieso nur rohe C-Arrays zu verwenden.

    – Gabriel Staples

    8. August 2021 um 22:46 Uhr


  • void array_operator ( const size_t count_, char arr [ static count_ ] ); das ist Standard-C. Darin hat man vollständige Array-Informationen. Plus arr muss mind count_ Elemente. Kontext ist diese Diskussion nicht “C is better” zu schimpfen.

    – Chef Gladiator

    12. August 2021 um 18:10 Uhr


  • Außerdem stört mich das auch: developercommunity.visualstudio.com/t/…

    – Chef Gladiator

    12. August 2021 um 18:30 Uhr

995320cookie-checkWas ist eine „Span“ und wann sollte ich eine verwenden?

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

Privacy policy