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?
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 span
Die 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 span
s, 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).
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:
- 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 …
- Warte, das klingt wie ein
std::array
wie ist ein span
anders als das?
- Oh, das erinnert mich, ist kein
std::vector
wie ein std::array
zu?
- 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 T
wohingegen 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 T
und 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:
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!):
- https://en.cppreference.com/w/cpp/container/span
std::array<T, N>
(Beachten Sie, dass es eine Fest Größe N
!):
- https://en.cppreference.com/w/cpp/container/array
- http://www.cplusplus.com/reference/array/array/
std::vector<T>
(wird bei Bedarf automatisch dynamisch vergrößert):
- https://en.cppreference.com/w/cpp/container/vector
- 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:
- Hauptseite: https://abseil.io/
- https://abseil.io/docs/cpp/
- GitHub-Repository: https://github.com/abseil/abseil-cpp
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:
- Struct mit Template-Variablen in C++
- Wikipedia: C++-Klassen
- Standardsichtbarkeit von C++-Klassen-/Strukturmitgliedern
Verwandt:
- [another one of my answers on templates and spans] Wie man Span of Spans macht
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, alsstd::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