Por que std :: ssize () foi introduzido em C ++ 20?

99

C ++ 20 introduziu a std::ssize()função livre conforme abaixo:

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

Uma possível implementação parece usar static_cast, para converter o valor de retorno da size()função-membro de cl ass C em sua contraparte assinada.

Visto que a size()função-membro de C sempre retorna valores não negativos, por que alguém iria querer armazená-los em variáveis ​​com sinal? Caso se queira mesmo, é uma questão de simples static_cast.

Por que é std::ssize()introduzido no C ++ 20?

John Z. Li
fonte
4
@ Jarod42 Não é a implementação definida em vez de indefinida? (estouro assinado é indefinido. mas a conversão assinada é definida pela implementação)
phön
8
Se eles adicionassem ssizeofoperador também.
geza
3
Isso pode estar relacionado: stackoverflow.com/questions/30395205/…
Marco13
10
@ JohnZ.Li Correndo o risco de soar muito pouco construtivo: Eu acho que todo o sistema de tipos do C ++ em relação aos tipos inteiros está quebrado. Claro, pode-se argumentar que algumas peculiaridades (como não saber quantos bits a charpossui) são herdadas de C e pelo menos um pouco atenuadas por (u)intX_t, mas ainda é uma fonte infinita de bugs igualmente sutis e críticos. Coisas como ssizesão apenas patches, e vai demorar um pouco (talvez "para sempre") até que isso afunde nos "guias de práticas recomendadas" que as pessoas (podem) seguir rigorosamente.
Marco13 de
6
@ Marco13: Por outro lado, o sistema de tipos C / C ++ (em oposição ao sistema de tipos fixos do Java, por exemplo), além de permitir que o código C / C ++ funcione em arquiteturas onde a maioria das outras linguagens falha, permite que instrutores competentes obtenham alguns lições na cabeça de um aluno. Tipo, nem todo mundo é de 64 bits. E não, nem todo o mundo usa caracteres de 8 bits. É muito fácil lidar com essas coisas e isso o torna um desenvolvedor melhor, bastando que os instrutores ensinem isso desde o início . (E, só para ter certeza, você não sabe que os (u)intX_ttipos são opcionais , não é?)
DevSolar

Respostas:

69

A justificativa é descrita neste artigo . Uma citação:

Quando span foi adotado em C ++ 17, ele usou um inteiro com sinal tanto como índice quanto como tamanho. Em parte, isso era para permitir o uso de "-1" como um valor sentinela para indicar um tipo cujo tamanho não era conhecido no momento da compilação. Mas ter um contêiner STL cuja função size () retornasse um valor assinado era problemático, então P1089 foi introduzido para "consertar" o problema. Recebeu o apoio da maioria, mas não a margem de 2 para 1 necessária para o consenso.

Este artigo, P1227, foi uma proposta para adicionar funções não membros std :: ssize e membro ssize (). A inclusão desses tornaria certos códigos muito mais simples e permitiria a prevenção de não sinais indesejados em cálculos de tamanho. A ideia era que a resistência a P1089 diminuiria se ssize () fosse disponibilizado para todos os contêineres, tanto por meio de std :: ssize () quanto como funções-membro.

Nadav Har'El
fonte
30
O for(int i = 0; i < container.ssize() - 1; ++i)exemplo também é bastante convincente
Caleth
7
@John, parece-me de fato que eles poderiam fazer a mesma coisa que string :: npos e apenas usar size_t (-1) como um valor especial.
rubenvb
15
@ JohnZ.Li Há muito tempo é considerado um erro que os tipos de tamanho STL não tenham sinais. Agora, infelizmente, é tarde demais para reformá-lo. Fornecer uma função gratuita é o melhor que podemos fazer a partir de agora.
LF de
16
@LF: Era Herb Sutter em uma conferência (talvez Bjarne tenha dito isso também). Mas ele está um pouco errado. Agora, com computadores de 32 bits / 64 bits, o tamanho assinado seria melhor (então ele está certo). Mas nos velhos tempos (tamanhos de 16 bits), o tamanho assinado teria sido ruim (por exemplo, poderíamos ter alocado matrizes de 32k bytes apenas).
geza
11
@LF: Descobri que Herb mencionou isso: youtube.com/watch?v=Puio5dly9N8&t=2667 . Quando ele diz que “na prática não surge muito”, é verdade hoje. Mas não era verdade há mais de 20 anos (sistemas de 16 bits). Portanto, não foi um grande erro usar sem sinal, quando o STL foi projetado.
geza
50

Roubado gratuitamente de Eric Niebler:

'Unsigned types signal that a negative index/size is not sane'era a sabedoria predominante quando o STL foi projetado pela primeira vez. Mas logicamente, uma contagem de coisas não precisa ser positiva. Posso querer manter uma contagem em um inteiro assinado para denotar o número de elementos adicionados ou removidos de uma coleção. Então, eu gostaria de combinar isso com o tamanho da coleção. Se o tamanho da coleção não é assinado, agora sou forçado a misturar aritmética assinada e não assinada, que é um bug farm. Os compiladores alertam sobre isso, mas como o design do STL praticamente força os programadores a essa situação, o aviso é tão comum que a maioria das pessoas o desativa. É uma pena, porque esconde bugs reais.

O uso de ints não assinados em interfaces não é a vantagem que muitas pessoas pensam que é. Se por acidente um usuário passa um número ligeiramente negativo para a API, de repente ele se torna um grande número positivo. Se a API considerou o número assinado, ela pode detectar a situação afirmando que o número é maior ou igual a zero.

Se restringirmos nosso uso de ints não assinados à manipulação de bits (por exemplo, máscaras) e usarmos ints assinados em qualquer outro lugar, é menos provável que ocorram bugs e sejam mais fáceis de detectar quando eles ocorrem.

sp2danny
fonte
6
O Swift adota essa abordagem, mesmo que não tenha a preocupação de números com sinais negativos serem reinterpretados como números não sinalizados massivos (uma vez que não há casts implícitos, que são o que realmente levam você a essa casa maluca para começar). Eles apenas adotam a abordagem de (tamanho de palavra de máquina) Intdeve ser o tipo de moeda comum para números inteiros, mesmo quando apenas números positivos fazem sentido (como indexar uma matriz). Qualquer desvio deve ser bem fundamentado. É bom não precisar se preocupar com elencos em todos os lugares.
Alexander - Reintegrar Monica