2015-10-05 12 views
11

Ho il seguente pezzo di codice:sottrazione di due unsigned dà firmato

#include <cstdint> 

template <typename T> 
T test(T a, T b) 
{ 
    float aabb = reinterpret_cast<float>(a - b); 
} 

int main(int argc, const char *argv[]) 
{ 
    std::uint8_t a8, b8; 
    test(a8, b8); 
    return 0; 
} 

So che il reinterpret_cast<float> non può funzionare e che dà un errore in fase di compilazione. Sto usando quell'errore in modo che il compilatore mi dica il tipo di a - b.

Il problema è che in questo caso, si dice che il tipo di a - b è int quando entrambi sono uint8_t (unsigned char). Lo stesso accade con uint16_t. Ma non con uint32_t che dice che a - b è unsigned int.

Quindi, la mia domanda è: è questo comportamento previsto (che il char senza segno - unsigned char fornisce un int), o si tratta di un qualche tipo di strano bug del compilatore (testato sia con GCC che con clang)?

+3

Questo sembra [rilevante] (http://stackoverflow.com/questions/29817927/why-are-integer-types-promoted-during-addition-in-c?rq=1) (la promozione è almeno simile in C e C++, se non uguale). – chris

+1

Sì, qualsiasi tipo più piccolo di 'int' promuove a' int' per l'aritmetica. – ShadowRanger

+0

Evita anche 'reinterpret_cast', è definito dall'implementazione. – vsoftco

risposta

11

Sì, questo è previsto, come parte dei cosiddetti usuali conversioni aritmetiche in combinazione con le regole per la promozione integrale .

Il testo esatto è cambiato tra C++ 03 e C++ 11, ma il risultato finale è lo stesso in questo caso.


[C++03: 4.5/1]: Un rvalue di tipo char, signed char, unsigned char, short int, o senza segno short int può essere convertito in un rvalue di tipo int se int può rappresentare tutti i valori del tipo di sorgente; in caso contrario, il valore di origine può essere convertito in un valore di tipo unsigned int.

[C++03: 4.5/5]: Queste conversioni sono chiamate promozioni .

Molti operatori binari che si aspettano operandi di tipo aritmetico o di enumerazione causano conversioni e producono tipi di risultato in modo simile. Lo scopo è quello di produrre un tipo comune, che è anche il tipo del risultato.

Questo modello è chiamato conversioni aritmetiche abituali, che sono definite come segue:

  • Se l'operando è di tipo long double, l'altro è convertito a long double.
  • In caso contrario, se uno degli operandi è double, l'altro deve essere convertito in doppio.
  • In caso contrario, se uno degli operandi è float, l'altro deve essere convertito in float.
  • In caso contrario, le promozioni integrali (4.5) devono essere eseguite su entrambi gli operandi.
  • Quindi, se uno degli operandi è unsigned long, l'altro deve essere convertito in unsigned long.
  • Altrimenti, se un operando è long int e l'altra unsigned int, quindi se un long int può rappresentare tutti i valori di un unsigned int, int non firmato è convertito in un long int; altrimenti entrambi gli operandi devono essere convertiti in unsigned long int.
  • In caso contrario, se uno degli operandi è long, l'altro deve essere convertito in long.
  • In caso contrario, se uno degli operandi è unsigned, l'altro deve essere convertito in unsigned.

[Nota: altrimenti, l'unico caso rimanente è che entrambi gli operandi sono int]


[C++11: 4.5/1]: Una prvalue di tipo intero diverso da bool, char16_t, char32_t o wchar_t il cui rank di conversione dei numeri interi (4.13) è inferiore al valore di int può essere convertito in un valore di tipo int se int può rappresentare tutti i valori del tipo di origine; in caso contrario, il valore di origine può essere convertito in un valore di tipo non firmato int.

[C++11: 4.5/7]: Queste conversioni sono chiamate promozioni .

[C++11: 5.9]: Molti operatori binari che si aspettano operandi di tipo aritmetico o di enumerazione causano conversioni e producono tipi di risultato in modo simile. Lo scopo è quello di produrre un tipo comune, che è anche il tipo del risultato.

Questo modello è chiamato conversioni aritmetiche abituali, che sono definite come segue:

  • Se uno degli operandi è di tipo censimento ambito (7.2), non vengono eseguite conversioni; se l'altro operando non ha lo stesso tipo, l'espressione è mal formata.
  • Se uno degli operandi è di tipo long double, l'altro deve essere convertito in long double.
  • Altrimenti, se uno degli operandi è doppio, l'altro deve essere convertito in doppio.
  • Altrimenti, se uno degli operandi è mobile, l'altro deve essere convertito in float.
  • In caso contrario, le promozioni integrali (4.5) devono essere eseguite su entrambi gli operandi. allora sarà applicata alle operandi promosse le seguenti regole:
    • Se entrambi gli operandi sono dello stesso tipo, non è necessaria alcuna ulteriore conversione.
    • Altrimenti, se entrambi gli operandi hanno tipi interi con segno o entrambi hanno tipi interi senza segno, l'operando con il tipo di rango di conversione intero minore deve essere convertito nel tipo dell'operando con grado maggiore.
    • Altrimenti, se l'operando che ha un numero intero senza segno ha rank superiore o uguale al rank del tipo di un altro operando, l'operando con tipo di intero con segno deve essere convertito nel tipo di operando con tipo di intero senza segno.
    • Altrimenti, se il tipo dell'operando con tipo intero con segno può rappresentare tutti i valori del tipo dell'operando con tipo intero senza segno, l'operando con tipo intero senza segno deve essere convertito nel tipo dell'operando con numero intero con segno genere.
    • In caso contrario, entrambi gli operandi devono essere convertiti nel tipo intero senza segno corrispondente al tipo di operando con tipo intero con segno.
+3

Oooh aspetta un minuto .. promozioni integrali per questo codice in entrambi i casi ... ooh-er –

+0

@LightnessRacesinOrbit, stavo solo guardando e chiedendo se è quello che è successo. Peccato, perché è stato emozionante imparare. – chris

+0

risolto; Ho sbagliato: P –

Problemi correlati