2016-04-25 13 views
11

Questo codice non fa quello che dovrebbe fare:Perché std: :(​​i) ostream considera il carattere firmato/non firmato come un testo e non un intero?

#include <iostream> 
#include <cstdint> 

int main() 
{ 
    uint8_t small_integer; 
    std::cin >> small_integer; 
    std::cout << small_integer; 
} 

La ragione è semplice: uint8_t è un typedef per unsigned char e flussi di trattare questo tipo come testo:
Visual C++ 2015 implementazione

template<class _Traits> inline 
    basic_istream<char, _Traits>& operator>>(
     basic_istream<char, _Traits>& _Istr, unsigned char& _Ch) 
    { // extract an unsigned char 
    return (_Istr >> (char&)_Ch); 
    } 

e un codice simile con cast char per operator <<.

Le mie domande:

  1. E` questo comportamento (streaming operatori trattando un signed/unsigned char come tipo di carattere e non un intero) richiesto dalla norma? Se è così:
    1. Qual è la logica alla base di tale semantica poco intuitiva?
    2. Se questo fosse considerato un difetto, c'erano proposte per cambiare questa semantica?

forse dovrei aggiungere un po 'di spiegazione perché ritengo controintuitivo. Sebbene il nome del tipo contenga la parola char, la parte signed o unsigned specifica una particolare semantica integer e tali tipi vengono generalmente utilizzati come numeri interi in byte. Anche lo standard definisce int8_t/uint8_t attraverso di loro.

UPD: La questione è sul comportamento di sovraccarichi operatore di streaming per unsigned char e signed char.

+1

È fastidioso. Ho usato la mia propria funzione to_string che tratta (u) int8_t come interi mentre tratta il carattere come un carattere. Ho aggiunto specializzazioni separate per uint8_t, int8_t e char dal momento che presumo che sia perfettamente valido per coloro che non devono essere di tre tipi distinti. – Matt

+4

Non è una risposta alla tua domanda, ma ['std :: byte'] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0257r0.pdf) è stato proposto di superare questo problema. – Praetorian

+0

@Praetorian, mentre questa è davvero una proposta interessante, affronta un problema assolutamente diverso. –

risposta

3

Lo standard (n3797) dice il seguente:

27.7.2.2.3 basic_istream :: operatore >>

template<class charT, class traits> 
basic_istream<charT,traits>& operator>>(basic_istream<charT,traits>& in, charT& c); 

template<class traits> 
basic_istream<char,traits>& operator>>(basic_istream<char,traits>& in, unsigned char& c); 

template<class traits> 
basic_istream<char,traits>& operator>>(basic_istream<char,traits>& in, signed char& c); 

12 E etti FF: si comporta come un formattato membro di input (come descritto in 27.7.2.2.1) di in. Dopo che un oggetto sentinella è stato creato un carattere viene estratto da in, se uno è disponibile e memorizzato in c. Altrimenti, la funzione chiama in.setstate (failbit).

27.7.3.6.4 Personaggio inseritore funzioni template

// specialization 
template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, char c); 

// signed and unsigned 
template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c); 

template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, unsigned char c); 

1 e ff etti: si comporta come una funzione di uscita formattato (27.7.3.6.1) di fuori. Costruisce una sequenza di caratteri seq. Se c ha tipo char e il tipo di carattere del flusso non è char, allora il seq consiste in out.widen (c); altrimenti seq è costituito da c. Determina il riempimento per seq come descritto in 27.7.3.6.1. Inserisce seq in fuori. Chiama os.width (0).

Quindi la risposta alla prima domanda: sì, il principio richiede che operator >> e operator << si comportano esattamente lo stesso per char, unsigned char e signed char, cioè leggono/scrivere un singolo carattere, non è un numero intero. Sfortunatamente, lo standard non spiega perché. Spero che qualcuno farà luce su 2 e 3.

1
  1. È questo comportamento richiesto dalla norma? Se è così:

Hai già risposto a questo. Sì, lo standard definisce come gli iostreams devono gestire i caratteri firmati e non firmati.

  1. Qual è la logica alla base di tale semantica poco intuitiva?

Perché signed char e unsigned char sono tipi di carattere, quindi sono sempre trattati come personaggi dalle classi iostreams.

L'indizio è nel nome: signed char è un tipo di carattere firmato. unsigned char è un tipo di carattere senza segno. Gli altri tipi interi hanno int nel loro nome (anche se a volte è facoltativo, ad esempio short e long unsigned sono identici a short int e long unsigned int rispettivamente).

Lo standard non ha bisogno di dire perché questo è vero, perché non è un documento di progettazione o una motivazione per la cronologia di C e C++, è una specifica.

Se si desidera un tipo che si comporta come un numero intero con soli 8 bit, sarà necessario crearne uno personalizzato (ad esempio utilizzando un tipo di enumerazione o una struttura che contiene un valore) e definire gli overload rilevanti dell'operatore.

  1. Se questo fosse considerato un difetto, c'erano proposte per cambiare questa semantica?

No, non credo. Sono sempre stati i tipi di carattere e si rompono troppi codici per cambiarlo.

Problemi correlati