¿Qué es un “lapso” y cuándo debería usar uno?

Recientemente recibí sugerencias para usar span 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 nada parecido en la biblioteca estándar de C ++.

Entonces, ¿qué es este misterioso span y por qué (o cuándo) es una buena idea usarlo si no es estándar?

¿Qué es?

Un span es:

  • Una abstracción muy ligera de una secuencia contigua de valores de tipo T en algún lugar de la memoria.
  • Básicamente una struct { T * const ptr; size_t length; } struct { T * const ptr; size_t length; } struct { T * const ptr; size_t length; } con un montón de métodos de conveniencia.
  • Un tipo no propietario (es decir, un “tipo de referencia” en lugar de un “tipo de valor”): nunca asigna ni desasigna nada y no mantiene vivos los punteros inteligentes.

Anteriormente se lo conocía como una array_view e incluso antes como una array_ref .

¿Cuándo debería usarlo?

Primero, cuando no usarlo:

  • No lo use en el código que podría tomar cualquier par de iteradores de inicio y fin, como std::sort , std::find_if , std::copy y todas esas funciones súper genéricas de plantilla.
  • No lo use si tiene un contenedor de biblioteca estándar (o un contenedor de Boost, etc.) que sepa que es el adecuado para su código. No pretende suplantar a ninguno de ellos.

Ahora para cuando realmente usarlo:

Use span (respectivamente, span ) en lugar de una T* autónoma (respectivamente const T* ) para la que tenga el valor de longitud. Por lo tanto, reemplace funciones como:

  void read_into(int* buffer, size_t buffer_size); 

con:

  void read_into(span buffer); 

¿Por qué debería usarlo? ¿Por qué es algo bueno?

Oh, las luces son increíbles! Usando un span

  • significa que puede trabajar con esa combinación puntero + longitud / inicio + puntero como lo haría con un contenedor de biblioteca estándar extravagante, por ejemplo:

    • for (auto& x : my_span) { /* do stuff */ }
    • std::find_if(my_span.begin(), my_span.end(), some_predicate);

    … pero con ninguno de los gastos generales incurridos en la mayoría de las clases de contenedor.

  • deja que el comstackdor haga más trabajo para ti a veces. Por ejemplo, esto:

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

    se convierte en esto:

     int buffer[BUFFER_SIZE]; read_into(buffer); 

    … que hará lo que quisieras que hiciera. Ver también la Pauta P.5 .

  • es la alternativa razonable para pasar const vector& a las 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 ++.

  • facilita el análisis estático, por lo que el comstackdor podría ayudarlo a detectar errores tontos.

  • permite la instrumentación de comstackción de depuración para la comprobación de límites de tiempo de ejecución (es decir, los métodos de span tendrán un código de comprobación de límites dentro de #ifndef NDEBUG#endif )
  • indica que su código (que usa el lapso) no posee el puntero.

Hay aún más motivación para usar span , que puedes encontrar en las directrices 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 básicas de C ++ , que solo ha ido tomando forma desde 2015. (Aunque, como señalan los comentaristas, tiene una historia anterior).

Entonces, ¿cómo lo uso si todavía no está en la biblioteca estándar?

Es parte de la Biblioteca de Soporte de las Directrices Centrales (GSL). Implementaciones:

  • El GSL de Microsoft / Neil Macintosh contiene una implementación independiente: gsl/span
  • GSL-Lite es una implementación de archivo único de GSL completo (no es tan grande, no se preocupe), incluido span .

Tenga en cuenta que puede usarlo con versiones anteriores del estándar de lenguaje: C ++ 11 y C ++ 14, no solo C ++ 17.


Lectura adicional: Puede encontrar todos los detalles y consideraciones de diseño en la propuesta oficial final (?), P0122R7: span: bounds-safe views para secuencias de objetos de Neal Macintosh y Stephan J. Lavavej. Aunque es un poco largo.