Recientemente recibí sugerencias para usar span<T>
's en mi código, o he visto algunas respuestas aquí en el sitio que usan span
' supuestamente algún tipo de contenedor. Pero no puedo encontrar algo así en la biblioteca estándar de C ++ 17.
Entonces, ¿qué es esto misterioso span<T>
y por qué (o cuándo) es una buena idea usarlo si no es estándar?
std::span
se propuso en 2017. Se aplica a C ++ 17 o C ++ 20. Consulte también P0122R5, span: vistas de límites seguros para secuencias de objetos . ¿De verdad quieres apuntar a ese idioma? Pasarán años antes de que los compiladores se pongan al día.gsl::span
lugar de hacerlostd::span
. Vea también mi respuesta a continuación.Respuestas:
¿Qué es?
A
span<T>
es:T
en algún lugar de la memoria.struct { T * ptr; std::size_t length; }
con un montón de métodos convenientes.Anteriormente se conocía como
array_view
e incluso antes comoarray_ref
.¿Cuándo debería usarlo?
Primero, cuando no usarlo:
std::sort
,std::find_if
,std::copy
y todas esas funciones con plantilla súper-genérica.Ahora para saber cuándo usarlo realmente:
¿Por qué debería usarlo? ¿Por qué es algo bueno?
¡Oh, los tramos son increíbles! Usando un
span
...significa que puede trabajar con esa combinación de puntero + longitud / inicio + fin como lo haría con un contenedor de biblioteca estándar sofisticado, p. ej.:
for (auto& x : my_span) { /* do stuff */ }
std::find_if(my_span.begin(), my_span.end(), some_predicate);
... pero sin ninguno de los gastos generales en que incurre la mayoría de las clases de contenedores.
permite que el compilador trabaje más por usted a veces. Por ejemplo, esto:
se convierte en esto:
... que hará lo que quieras que haga. Véase también la directriz P.5 .
es la alternativa razonable a pasar
const vector<T>&
a funciones cuando espera que sus datos sean contiguos en la memoria. ¡No más ser regañado por los grandes y poderosos gurús de C ++!span
, los métodos tendrán algún código de verificación de límites dentro de#ifndef NDEBUG
...#endif
)Hay aún más motivación para usar
span
s, que puedes encontrar en las pautas básicas de C ++ , pero captas la deriva.¿Por qué no está en la biblioteca estándar (a partir de C ++ 17)?
Está en la biblioteca estándar, pero solo a partir de C ++ 20. La razón es que todavía es bastante nueva en su forma actual, concebida en conjunto con el proyecto de directrices centrales de C ++ , que solo ha estado tomando forma desde 2015. (Aunque como señalan los comentaristas, tiene una historia anterior).
Entonces, ¿cómo lo uso si aún no está en la biblioteca estándar?
Es parte de la Biblioteca de soporte de las Pautas principales (GSL). Implementaciones:
gsl/span
span<T>
.La implementación de GSL generalmente asume una plataforma que implementa soporte C ++ 14 [ 14 ]. Estas implementaciones alternativas de encabezado único no dependen de las instalaciones de GSL:
martinmoene/span-lite
requiere C ++ 98 o posteriortcbrindle/span
requiere C ++ 11 o posteriorTenga en cuenta que estas implementaciones de span diferentes tienen algunas diferencias en los métodos / funciones de soporte que vienen con; y también pueden diferir algo de la versión que entra en la biblioteca estándar en C ++ 20.
Lecturas adicionales: puede encontrar todos los detalles y consideraciones de diseño en la propuesta oficial final antes de C ++ 17, P0122R7: span: vistas seguras para las secuencias de objetos de Neal Macintosh y Stephan J. Lavavej. Aunque es un poco largo. Además, en C ++ 20, la semántica de comparación de tramo cambió (siguiendo este breve artículo de Tony van Eerd).
fuente
std::cout << sizeof(buffer) << '\n'
y verá que obtiene 100 sizeof (int) 's.std::array
es un contenedor, posee los valores.span
no es propietariostd::array
es una bestia completamente diferente. Su longitud se fija en tiempo de compilación y es un tipo de valor en lugar de un tipo de referencia, como explicó Caleth.@einpoklum hace un muy buen trabajo al presentar lo que
span
hay en su respuesta aquí . Sin embargo, incluso después de leer su respuesta, es fácil para alguien nuevo tener una secuencia de preguntas de flujo de pensamiento que no se responden completamente, como las siguientes:span
diferente de una matriz C? ¿Por qué no solo usar uno de esos? Parece que es solo uno de esos con el tamaño conocido también ...std::array
, ¿cómo esspan
diferente de eso?std::vector
como unstd::array
también?span
?Entonces, aquí hay alguna claridad adicional sobre eso:
CITA DIRECTA DE SU RESPUESTA - CON MIS ADICIONES EN NEGRITA :
Esas partes audaces son críticas para la comprensión de uno, ¡así que no las extrañe ni las lea mal! A
span
NO es una matriz C de estructuras, ni es una estructura de una matriz C de tipoT
más la longitud de la matriz (esto sería esencialmente lostd::array
que es el contenedor ), NOR es una matriz C de estructuras de punteros escribirT
más la longitud, sino que es una estructura única que contiene un solo puntero para escribirT
, y la longitud , que es el número de elementos (de tipoT
) en el bloque de memoria contigua al queT
apunta el puntero . De esta manera, la única sobrecarga que ha agregado utilizando unspan
son las variables para almacenar el puntero y la longitud, y cualquier función de acceso conveniente que utilice quespan
proporciona.Esto es UNLIKE a
std::array<>
porquestd::array<>
realmente asigna memoria para todo el bloque contiguo, y es UNLIKEstd::vector<>
porquestd::vector
básicamente es solo unstd::array
que también hace un crecimiento dinámico (generalmente duplicando su tamaño) cada vez que se llena e intenta agregarle algo más. . Astd::array
tiene un tamaño fijo, y aspan
ni siquiera administra la memoria del bloque al que apunta, solo apunta al bloque de memoria, sabe cuánto dura el bloque de memoria, sabe qué tipo de datos hay en una matriz C. en la memoria, y proporciona funciones de acceso convenientes para trabajar con los elementos en esa memoria contigua .Se es parte de la C ++ estándar:
std::span
es parte del estándar C ++ a partir de C ++ 20. Puede leer su documentación aquí: https://en.cppreference.com/w/cpp/container/span . Para ver cómo usar Googleabsl::Span<T>(array, length)
en C ++ 11 o posterior hoy , vea a continuación.Descripciones resumidas y referencias clave:
std::span<T, Extent>
(Extent
= "el número de elementos en la secuencia, ostd::dynamic_extent
si es dinámico". Un intervalo solo apunta a la memoria y facilita el acceso, ¡pero NO lo gestiona!):std::array<T, N>
(¡tenga en cuenta que tiene un tamaño fijoN
!):std::vector<T>
(automáticamente aumenta de tamaño dinámicamente según sea necesario):¿Cómo puedo usar
span
en C ++ 11 o posterior hoy ?Google ha abierto sus bibliotecas internas de C ++ 11 en forma de su biblioteca "Abseil". Esta biblioteca está diseñada para proporcionar características de C ++ 14 a C ++ 20 y posteriores que funcionan en C ++ 11 y versiones posteriores, para que pueda usar las características del mañana, hoy. Ellos dicen:
Aquí hay algunos recursos y enlaces clave:
span.h
encabezado yabsl::Span<T>(array, length)
clase de plantilla: https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189fuente