web-dev-qa-db-fra.com

Qu'est-ce qu'un "span" et quand devrais-je en utiliser un?

Récemment, j'ai eu des suggestions pour utiliser span<T> dans mon code, ou j'ai vu des réponses ici sur le site qui utilisent span - soi-disant une sorte de conteneur. Mais - je ne trouve rien de tel dans la bibliothèque standard C++.

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?

187
einpoklum

Qu'Est-ce que c'est?

Un span<T> est:

  • Une abstraction très légère d'une séquence contiguë de valeurs de type T quelque part en mémoire.
  • Fondamentalement, un struct { T * ptr; size_t length; } avec un tas de méthodes pratiques.
  • Un type non propriétaire (c'est-à-dire un "type de référence" plutôt qu'un "type de valeur"): il n'alloue ni ne désaffecte rien et ne maintient pas les pointeurs intelligents en vie.

Il était auparavant appelé array_view et même plus tôt array_ref .

Quand devrais-je l'utiliser?

D'abord, quand pas pour l'utiliser:

  • Ne l'utilisez pas dans un code pouvant prendre n'importe quelle paire d'itérateurs de début et de fin, tels que std::sort, std::find_if, std::copy et toutes ces fonctions basées sur des modèles super-génériques.
  • Ne l'utilisez pas si vous avez un conteneur de bibliothèque standard (ou un conteneur Boost, etc.) que vous savez parfaitement adapté à votre code. Ce n'est pas destiné à supplanter aucun d'eux.

Maintenant pour quand l'utiliser réellement:

Utilisez span<T> (respectivement, span<const T>) au lieu d'un T* libre (respectivement const T*) pour lequel vous avez la valeur de longueur. Donc, remplacez des fonctions comme:

  void read_into(int* buffer, size_t buffer_size);

avec:

  void read_into(span<int> buffer);

Pourquoi devrais-je l'utiliser? Pourquoi est-ce une bonne chose?

Oh, les travées sont géniales! Utiliser un span...

  • signifie que vous pouvez utiliser cette combinaison pointeur + longueur/début + fin, comme vous le feriez avec un conteneur de bibliothèque standard sophistiqué et fantaisiste, 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 que la plupart des classes de conteneurs encourent.

  • laisse parfois le compilateur faire plus de travail pour vous. Par exemple, ceci:

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

    devient ceci:

    int buffer[BUFFER_SIZE];
    read_into(buffer);
    

    ... qui fera ce que vous voudriez qu'il fasse. Voir aussi directive P.5 .

  • est l’alternative raisonnable de passer const vector<T>& à des fonctions lorsque vous vous attendez à ce que vos données soient contiguës en mémoire. Pas plus se faire gronder par les gourous C++ haut et puissant.

  • facilite l’analyse statique, de sorte que le compilateur puisse vous aider à attraper des bugs stupides.
  • autorise l'instrumentation de compilation-débogage pour la vérification des limites à l'exécution (c'est-à-dire que les méthodes de span auront un code de vérification des limites dans #ifndef NDEBUG ... #endif)
  • indique que votre code (qui utilise l'étendue) ne possède pas le pointeur.

Il y a encore plus de motivation à utiliser spans, ce que vous pouvez trouver dans directives de base de C++ - mais vous voyez la dérive.

Pourquoi n'est-il pas dans la bibliothèque standard (à partir de C++ 17)?

C'est dans la bibliothèque standard - mais seulement à partir de C++ 20. La raison en est qu’elle est encore relativement nouvelle dans sa forme actuelle, conçue en conjonction avec le projet C++ core guidelines , qui ne prend forme que depuis 2015. (Bien que, comme le soulignent les commentateurs, elle a déjà été réalisée .)

Alors, comment puis-je l'utiliser s'il ne se trouve pas encore dans la bibliothèque standard?

Cela fait partie de GSL), la bibliothèque de support de --- (Core Guidelines . Implémentations:

  • Microsoft/Neil Macintosh's GSL contient une implémentation autonome: gsl/span
  • GSL-Lite est une implémentation à un en-tête de l'ensemble du GSL (ce n'est pas si grand, ne vous inquiétez pas), y compris span<T>.

L’implémentation GSL suppose généralement une plate-forme implémentant le support C++ 14 [ 14 ]. Ces implémentations alternatives à en-têtes simples ne dépendent pas des fonctionnalités GSL:

Notez que vous pouvez l'utiliser avec les versions antérieures de la norme de langage - C++ 11 et C++ 14, pas seulement C++ 17.


Pour en savoir plus: Vous trouverez tous les détails et toutes les considérations de conception dans la proposition officielle finale avant C++ 17, P0122R7: span: bounds -safe vues pour les séquences d'objets par Neal Macintosh et Stephan J. Lavavej. C'est un peu long cependant. De plus, en C++ 20, la sémantique de la comparaison d'étendue a changé (à la suite de ce court article de Tony van Eerd).

215
einpoklum