¿Por qué se introdujo std :: ssize () en C ++ 20?

99

C ++ 20 introdujo la std::ssize()función gratuita de la siguiente manera:

template <class C>
    constexpr auto ssize(const C& c)
        -> std::common_type_t<std::ptrdiff_t,
                              std::make_signed_t<decltype(c.size())>>;

Una posible implementación parece utilizar static_castpara convertir el valor de retorno de la size()función miembro de cl ass C en su contraparte firmada.

Dado que la size()función miembro de C siempre devuelve valores no negativos, ¿por qué alguien querría almacenarlos en variables firmadas? En caso de que uno realmente quiera, es una cuestión de simple static_cast.

¿Por qué se std::ssize()introduce en C ++ 20?

John Z. Li
fuente
4
@ Jarod42 ¿No está definida la implementación en lugar de indefinida? (el desbordamiento firmado no está definido, pero la conversión firmada está definida por la implementación)
phön
8
Si solo agregan ssizeofoperador también.
geza
3
Esto podría estar algo relacionado: stackoverflow.com/questions/30395205/…
Marco13
10
@ JohnZ.Li A riesgo de sonar demasiado poco constructivo: creo que todo el sistema de tipos de C ++ con respecto a los tipos enteros está roto. Claro, se puede argumentar que algunas peculiaridades (como no saber cuántos bits chartiene) se heredan de C y al menos se alivian un poco (u)intX_t, pero sigue siendo una fuente inagotable de errores igualmente sutiles y críticos. Cosas como ssizeson solo parches, y tomará un tiempo (tal vez "para siempre") hasta que esto se sumerja en las "guías de mejores prácticas" comunes que la gente (puede) seguir rigurosamente.
Marco13
6
@ Marco13: Por otro lado, el sistema de tipo C / C ++ (en contraposición al sistema de tipos fijos por ejemplo de Java), aparte de permitir C / C ++ código para trabajar en arquitecturas donde la mayoría de otros idiomas croan, no permite competentes instructores para conseguir un poco importante lecciones en la cabeza de un estudiante. No todo el mundo es de 64 bits. Y no, no todo el mundo usa caracteres de 8 bits. Es muy fácil lidiar con estas cosas y te convierte en un mejor desarrollador, si tan solo los instructores te enseñaran esto desde el principio . (Y, sólo para asegurarse, que no sabe que los (u)intX_ttipos son opcionales , ¿verdad?)
DevSolar

Respuestas:

69

La justificación se describe en este documento . Una cita:

Cuando se adoptó span en C ++ 17, se utilizó un entero con signo tanto como índice como como tamaño. En parte, esto fue para permitir el uso de "-1" como valor centinela para indicar un tipo cuyo tamaño no se conocía en el momento de la compilación. Pero tener un contenedor STL cuya función size () devolvía un valor firmado era problemático, por lo que se introdujo P1089 para "solucionar" el problema. Recibió el apoyo de la mayoría, pero no el margen de 2 a 1 necesario para el consenso.

Este documento, P1227, fue una propuesta para agregar funciones no miembro std :: ssize y miembro ssize (). La inclusión de estos haría que cierto código sea mucho más sencillo y permitiría evitar la falta de firma no deseada en los cálculos de tamaño. La idea era que la resistencia a P1089 disminuiría si ssize () estuviera disponible para todos los contenedores, tanto a través de std :: ssize () como como funciones miembro.

Nadav Har'El
fuente
30
El for(int i = 0; i < container.ssize() - 1; ++i)ejemplo también es bastante convincente
Caleth
7
@John, me parece que de hecho podrían hacer lo mismo que string :: npos y simplemente usar size_t (-1) como un valor especial.
rubenvb
15
@ JohnZ.Li Durante mucho tiempo se ha considerado un error que los tipos de tamaño STL no estén firmados. Ahora, lamentablemente, es demasiado tarde para reformarlo. Proporcionar una función gratuita es lo mejor que podemos hacer a partir de ahora.
LF
16
@LF: Fue Herb Sutter en una conferencia (tal vez Bjarne también dijo esto). Pero está un poco equivocado. Ahora, con computadoras de 32 bits / 64 bits, el tamaño firmado sería mejor (así que tiene razón). Pero en los viejos tiempos (tamaños de 16 bits), el tamaño firmado habría sido malo (por ejemplo, podríamos haber asignado solo matrices de 32k bytes).
geza
11
@LF: He descubierto que Herb menciona esto: youtube.com/watch?v=Puio5dly9N8&t=2667 . Cuando dice que "no surge mucho en la práctica", es cierto hoy en día. Pero no era cierto> hace 20 años (sistemas de 16 bits) en absoluto. Por lo tanto, no fue un gran error usar unsigned, cuando se diseñó el STL.
geza
50

Gratuitamente robada de Eric Niebler:

'Unsigned types signal that a negative index/size is not sane'fue la sabiduría predominante cuando se diseñó el STL por primera vez. Pero, lógicamente, un recuento de cosas no tiene por qué ser positivo. Es posible que desee mantener un recuento en un entero con signo para indicar el número de elementos agregados o eliminados de una colección. Entonces me gustaría combinar eso con el tamaño de la colección. Si el tamaño de la colección no está firmado, ahora me veo obligado a mezclar aritmética firmada y no firmada, que es una granja de errores. Los compiladores advierten sobre esto, pero debido a que el diseño del STL obliga a los programadores a entrar en esta situación, la advertencia es tan común que la mayoría de la gente la apaga. Es una pena porque esto esconde errores reales.

El uso de entradas sin firmar en interfaces no es la bendición que mucha gente cree que es. Si por accidente un usuario pasa un número ligeramente negativo a la API, de repente se convierte en un gran número positivo. Si la API hubiera tomado el número como firmado, entonces puede detectar la situación afirmando que el número es mayor o igual a cero.

Si restringimos nuestro uso de entradas sin firmar a la alteración de bits (por ejemplo, máscaras) y usamos entradas firmadas en cualquier otro lugar, es menos probable que ocurran errores y es más fácil detectarlos cuando ocurren.

sp2danny
fuente
6
Swift adopta este enfoque, aunque no le preocupa que los números con signo negativo se reinterpreten como números masivos sin signo (ya que no hay conversiones implícitas, que son lo que realmente te lleva a esta loca casa de diversión para empezar). Simplemente adoptan el enfoque de que (tamaño de palabra de máquina) Intdeberían ser los tipos de moneda común de números enteros, incluso cuando solo los números positivos tienen sentido (como indexar una matriz). Cualquier desviación del mismo debe estar bien fundada. Es bueno no tener que preocuparse por los yesos en todas partes.
Alexander - Reincorpora a Monica
3
@ JohnZ.Li De hecho, "unsigned int considerado dañino para Java"
Nayuki