Primeiro, suponha que eu tenha
int16_t a;
int8_t b;
. Gostaria de fazer isso b=a;
descartando todos os MSBs, incluindo o bit de sinal, que a
não se encaixam em b
. Qual seria uma maneira correta de fazer isso?
Não consigo encontrar nenhuma informação útil e recente sobre este tópico. Este link diz que a conversão de estreitamento de sinal depende do compilador, mas isso acontece em C, não em C++. Também não quero comportamentos dependentes do compilador. Aqui diz que a conversão de estreitamento deveria ser um erro. Estou bastante confuso sobre o que fazer.
Segundo, se ambos a
e b
fossem suas contrapartes sem sinal, então seria b=a;
uma abordagem adequada? De acordo com o primeiro link, isso é definido pelo menos em C.
A partir do C++20, você pode simplesmente fazer isso
b=a;
e obter o comportamento correto. O=
operador realiza a conversão implícita para o tipo do seu operando esquerdo [expr.ass p3 do rascunho padrão N4860]. A conversão implícita inclui conversões integrais [conv p1.2]. A conversão integral, neste caso, resulta em:Matematicamente, esse é exatamente o efeito de truncar os bits altos, e os compiladores o implementam com um truncamento simples.
Você pode, se desejar, usar uma conversão estática explícita:
Isso pode evitar avisos de alguns compiladores; o efeito real é o mesmo.
A observação que você encontrou sobre a proibição de conversões de restrição aplica-se apenas à inicialização de listas , por exemplo
int8_t b{a};
. Não é relevante para atribuições simples, que realizam conversões de restrição sem problemas.Antes do C++20, a conversão de inteiros causava um comportamento definido pela implementação se o valor convertido estivesse fora do intervalo para o tipo de destino. Até onde eu sei, todos os compiladores tradicionais em plataformas tradicionais definiam o comportamento como truncamento, exatamente como foi codificado pelo C++20, portanto, na prática,
b=a;
funcionará corretamente também na maioria das implementações anteriores ao C++20.Se isso não for suficiente, você pode começar convertendo o valor para o tipo unsigned
uint8_t
, cujo comportamento para valores fora do intervalo sempre foi o truncamento, de volta para pelo menos C++98. Em seguida, faça algumas contas, que esperamos que sejam otimizadas, para garantir que você atribua ab
um valor que esteja dentro do intervalo:Experimente com godbolt e observe que, de fato, a variável temporária é otimizada e nenhuma subtração é feita; ela otimiza para uma carga de 16 bits e um armazenamento de 8 bits.