Récemment, j'ai reçu des suggestions d'utilisation de span<T>
's dans mon code, ou j'ai vu quelques réponses ici sur le site qui utilisent span
' s - soi-disant une sorte de conteneur. Mais - je ne trouve rien de tel dans la bibliothèque standard C ++ 17.
Alors, quel est ce mystérieux span<T>
, et pourquoi (ou quand) est-ce une bonne idée de l'utiliser si ce n'est pas standard?
std::span
a été proposé en 2017. Il s'applique au C ++ 17 ou au C ++ 20. Voir également P0122R5, vues span: sans limites pour les séquences d'objets . Voulez-vous vraiment cibler cette langue? Il faudra des années avant que les compilateurs ne rattrapent leur retard.gsl::span
plutôt questd::span
. Voir aussi ma réponse ci-dessous.Réponses:
Qu'Est-ce que c'est?
A
span<T>
est:T
quelque part en mémoire.struct { T * ptr; std::size_t length; }
avec un tas de méthodes pratiques.Il était auparavant connu sous le nom de
array_view
et même plus tôtarray_ref
.Quand devrais-je l'utiliser?
Tout d'abord, quand ne pas l'utiliser:
std::sort
,std::find_if
,std::copy
et toutes les fonctions de super-générique basé sur un modèle.Maintenant, quand l'utiliser réellement:
Pourquoi devrais-je l'utiliser? Pourquoi est-ce une bonne chose?
Oh, les portées sont géniales! À l'aide d'un
span
...signifie que vous pouvez travailler avec cette combinaison pointeur + longueur / pointeur de début + fin comme vous le feriez avec un conteneur de bibliothèque standard fantaisie et pimped-out, par exemple:
for (auto& x : my_span) { /* do stuff */ }
std::find_if(my_span.begin(), my_span.end(), some_predicate);
... mais avec absolument aucun des frais généraux encourus par la plupart des classes de conteneurs.
laisse parfois le compilateur faire plus de travail pour vous. Par exemple, ceci:
devient ceci:
... qui fera ce que vous voudriez qu'il fasse. Voir également la ligne directrice P.5 .
est l'alternative raisonnable au passage
const vector<T>&
aux fonctions lorsque vous vous attendez à ce que vos données soient contiguës en mémoire. Plus besoin d'être grondé par les gourous C ++ haut et puissants!span
les méthodes auront du code de vérification des limites dans#ifndef NDEBUG
...#endif
)Il y a encore plus de motivation pour utiliser
span
s, que vous pouvez trouver dans les directives de base C ++ - mais vous attrapez la dérive.Pourquoi n'est-il pas dans la bibliothèque standard (à partir de C ++ 17)?
Il se trouve dans la bibliothèque standard - mais uniquement depuis C ++ 20. La raison en est qu'il est encore assez nouveau dans sa forme actuelle, conçu en collaboration avec le projet de lignes directrices de base C ++ , qui ne prend forme que depuis 2015. (Bien que les commentateurs le soulignent, il a une histoire antérieure.)
Alors, comment puis-je l'utiliser si ce n'est pas encore dans la bibliothèque standard?
Il fait partie de la bibliothèque de support (GSL) de Core Guidelines . Implémentations:
gsl/span
span<T>
.L'implémentation GSL suppose généralement une plate-forme qui implémente le support C ++ 14 [ 14 ]. Ces implémentations alternatives à en-tête unique ne dépendent pas des fonctionnalités GSL:
martinmoene/span-lite
nécessite C ++ 98 ou version ultérieuretcbrindle/span
nécessite C ++ 11 ou version ultérieureNotez que ces différentes implémentations span ont quelques différences dans les méthodes / fonctions de support avec lesquelles elles sont fournies; et ils peuvent également différer quelque peu de la version entrant dans la bibliothèque standard en C ++ 20.
Pour en savoir plus: Vous pouvez trouver tous les détails et les considérations de conception dans la proposition officielle finale avant C ++ 17, P0122R7: span: vues sans limites pour les séquences d'objets de Neal Macintosh et Stephan J. Lavavej. C'est un peu long cependant. De plus, en C ++ 20, la sémantique de comparaison des intervalles a changé (suite à ce court article de Tony van Eerd).
la source
std::cout << sizeof(buffer) << '\n'
et vous verrez que vous obtenez 100 sizeof (int).std::array
est un conteneur, il possède les valeurs.span
n'est pas propriétairestd::array
est une bête complètement différente. Sa longueur est fixée au moment de la compilation et c'est un type de valeur plutôt qu'un type de référence, comme l'a expliqué Caleth.@einpoklum fait un assez bon travail d'introduire quelle
span
est sa réponse ici . Cependant, même après avoir lu sa réponse, il est facile pour quelqu'un qui ne connaît pas le domaine d'avoir toujours une séquence de questions de réflexion qui ne sont pas entièrement répondues, telles que les suivantes:span
différent d'un tableau C? Pourquoi ne pas simplement en utiliser un? Il semble que ce ne soit que l'un de ceux dont la taille est également connue ...std::array
, en quoi est-cespan
différent de cela?std::vector
comme unstd::array
aussi?span
?Alors, voici quelques précisions supplémentaires à ce sujet:
CITATION DIRECTE DE SA RÉPONSE - AVEC MES ADDITIONS EN GRAS :
Ces parties audacieuses sont essentielles à la compréhension, alors ne les manquez pas ou ne les lisez pas! A
span
n'est PAS un C-tableau de structures, ni un struct d'un C-tableau de typeT
plus la longueur du tableau (ce serait essentiellement ce que lestd::array
conteneur est), NI n'est-ce pas un C-tableau de structures de pointeurs à taperT
plus la longueur, mais c'est plutôt une structure unique contenant un seul pointeur à taperT
, et la longueur , qui est le nombre d'éléments (de typeT
) dans le bloc de mémoire contigu vers lequelT
pointe le pointeur à taper ! De cette façon, la seule surcharge que vous avez ajoutée en utilisant unspan
sont les variables pour stocker le pointeur et la longueur, et toutes les fonctions d'accesseur de commodité que vous utilisez et qui lespan
fournissent.Il ne s'agit pas d'un
std::array<>
parce que lestd::array<>
alloue réellement de la mémoire pour le bloc contigu entier, et il n'est pas commestd::vector<>
parce qu'unstd::vector
est fondamentalement juste unstd::array
qui fait également une croissance dynamique (généralement doublant de taille) à chaque fois qu'il se remplit et que vous essayez d'ajouter quelque chose d'autre. . Astd::array
est de taille fixe, et unspan
ne gère même pas la mémoire du bloc il pointe, il seulement des points au bloc de mémoire, sait combien de temps le bloc de mémoire est, sait ce type de données est dans un C-tableau dans la mémoire et fournit des fonctions d'accesseur pratiques pour travailler avec les éléments de cette mémoire contiguë .Il fait partie du standard C ++:
std::span
fait partie de la norme C ++ à partir de C ++ 20. Vous pouvez lire sa documentation ici: https://en.cppreference.com/w/cpp/container/span . Pour voir comment utiliser Googleabsl::Span<T>(array, length)
en C ++ 11 ou plus tard aujourd'hui , voir ci-dessous.Descriptions sommaires et références clés:
std::span<T, Extent>
(Extent
= "le nombre d'éléments dans la séquence, oustd::dynamic_extent
s'il est dynamique". Une plage pointe simplement vers la mémoire et facilite l'accès, mais ne la gère PAS!):std::array<T, N>
(remarquez qu'il a une taille fixeN
!):std::vector<T>
(augmente automatiquement la taille de façon dynamique si nécessaire):Comment puis-je utiliser
span
en C ++ 11 ou plus tard aujourd'hui ?Google a open source leurs bibliothèques C ++ 11 internes sous la forme de leur bibliothèque "Abseil". Cette bibliothèque est destinée à fournir des fonctionnalités C ++ 14 à C ++ 20 et au-delà qui fonctionnent en C ++ 11 et versions ultérieures, afin que vous puissiez utiliser les fonctionnalités de demain dès aujourd'hui. Ils disent:
Voici quelques ressources et liens clés:
span.h
en-tête etabsl::Span<T>(array, length)
classe de modèle: https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189la source