2012-04-14 19 views
31

Apparentemente il compilatore li considera come tipi non correlati e quindi è richiesto reinterpret_cast. Perché questa è la regola?Perché non posso static_cast tra char * e unsigned char *?

+0

Sto prendendo l'hash SHA-1 di una stringa. 'c_str()' restituisce un 'const char *' e la funzione SHA-1 accetta un 'const unsigned char *' come argomento. – Nick

+0

E cosa ti aspetti che succeda se quella stringa contiene valori di caratteri negativi? – Pubby

+0

Mi aspetto che il valore negativo 'c' diventi' c + 256', come è normale nella conversione di un byte con segno in uno senza segno. Onestamente, sto solo facendo la conversione per calcolare un valore hash. Non mi interessa come vengono convertiti, purché vengano convertiti allo stesso modo ogni volta. – Nick

risposta

26

sono completamente diversi tipi vedono di serie:

3.9.1 tipi fondamentali [basic.fundamental]

1 oggetti dichiarati come caratteri char) devono essere abbastanza grandi da negozio di qualsiasi membro del set di caratteri di base dell'implementazione. Se un carattere da questo set è memorizzato in un oggetto carattere, il valore integrale di quell'oggetto carattere è uguale al valore del singolo carattere letterale di quel carattere. È l'implementazione definita se un oggetto char può contenere valori negativi . I personaggi possono essere dichiarati esplicitamente senza segno o
firmato.Plain char, signed char e unsigned char sono tre tipi distinti. Un carattere, un carattere firmato e un carattere non firmato occupano la stessa quantità di memoria e hanno lo stesso allineamento requisiti (basic.types); cioè, hanno la stessa rappresentazione dell'oggetto . Per i tipi di carattere, tutti i bit dell'oggetto rappresentazione
partecipano alla rappresentazione del valore. Per i tipi di carattere senza segno, tutti i possibili schemi di bit della rappresentazione del valore rappresentano numeri. Questi requisiti non valgono per altri tipi. In qualsiasi implementazione particolare, un oggetto char semplice può assumere sia gli stessi valori di un carattere firmato o di un carattere non firmato; quale è definito dall'implementazione.

Quindi analogo a questo è anche il motivo per il seguente fallisce:

unsigned int* a = new unsigned int(10); 
int* b = static_cast<int*>(a); // error different types 

a e b sono tipi completamente diversi, in realtà quello che si stanno mettendo in discussione è il motivo per cui è static_cast così restrittiva quando può effettuare le seguenti operazioni, senza problema

unsigned int a = new unsigned int(10); 
int b = static_cast<int>(a); // OK but may result in loss of precision 

e perché può non dedurre che i tipi di destinazione sono la stessa larghezza campo di bit e possono essere rappresentati? Può farlo per i tipi scalari ma per i puntatori, a meno che il target non sia derivato dalla fonte e si desideri eseguire un downcast, quindi il casting tra i puntatori non funzionerà.

Bjarne Stroustrop afferma il motivo per cui static_cast 's sono utili in questo link: http://www.stroustrup.com/bs_faq2.html#static-cast ma in forma abbreviata è per l'utente di indicare chiaramente quali sono le loro intenzioni e dare il compilatore l'opportunità di verificare che quello che avete intenzione può essere raggiunto, dal momento che static_cast non supporta il casting tra diversi tipi di puntatori, il compilatore può rilevare questo errore per avvisare l'utente e se vuole veramente fare questa conversione, dovrebbe usare reinterpret_cast.

+0

thx per indicare lo standard qui. Non ce l'ho disponibile. –

+0

Quindi, se sono tipi distinti, perché il compilatore consente il cast 'unsigned char a = 255; char b = static_cast (a); '? – Nick

+2

lo stesso motivo per cui puoi passare static_cast da double a int e viceversa, ciò che non puoi fare è static_cast double * to int *, i tipi di puntatore sono diversi ma puoi convertire da un valore all'altro con l'avvertenza che potrebbe essere una perdita di precisione – EdChum

7

si sta provando a convertire puntatori non correlati con un static_cast. Questo non è ciò che è per static_cast. Qui puoi vedere: Type Casting.

Con static_cast è possibile convertire i dati numerici (ad esempio char in unsigned char dovrebbe funzionare) o puntatori alle classi correlate (correlate da alcune ereditarietà). Questo non è il caso. Si desidera convertire un puntatore non correlato in un altro, quindi è necessario utilizzare reinterpret_cast.

Fondamentalmente quello che stai cercando di fare è per il compilatore lo stesso che provare a convertire un char * in un void *.


Ok, ecco alcune considerazioni aggiuntive per cui consentire questo è fondamentalmente sbagliato. static_cast può essere utilizzato per convertire tipi numerici l'uno nell'altro. Quindi è perfettamente legale per scrivere il seguente:

char x = 5; 
unsigned char y = static_cast<unsigned char>(x); 

ciò che è anche possibile:

double d = 1.2; 
int i = static_cast<int>(d); 

Se si guarda a questo codice in assembler vedrete che il secondo cast non è un semplice re -interpretazione del modello di bit di d, ma al suo posto sono inserite alcune istruzioni di assemblatore per le conversioni.

Ora, se estendiamo questo comportamento agli array, il caso in cui è sufficiente un diverso modo di interpretare il modello di bit, potrebbe funzionare. Ma per quanto riguarda il cast di array di doppi in array di ints? Ecco dove devi dichiarare di volere semplicemente una reinterpretazione dei pattern di bit - esiste un meccanismo chiamato reinterpret_cast, oppure devi fare del lavoro extra. Come puoi vedere, l'estensione del static_cast per puntatore/array non è sufficiente poiché deve comportarsi in modo simile ai singoli valori static_casting dei tipi. Questo a volte richiede un codice aggiuntivo e non è chiaramente definibile come dovrebbe essere fatto per gli array. Nel tuo caso - stop a \ 0 - perché è la convention? Questo non è sufficiente per casi non stringa (numero). Cosa succederà se la dimensione del tipo di dati cambia (ad es. Int contro double su x86-32bit)?

Il comportamento desiderato non può essere definito correttamente per tutti i casi d'uso, ecco perché non è nello standard C++. Altrimenti dovresti ricordare cose come: "Posso lanciare questo tipo sull'altro purché siano di tipo intero, abbiano la stessa larghezza e ...". In questo modo è assolutamente chiaro - o sono CLASSI correlati - quindi puoi lanciare i puntatori, o sono tipi numerici - quindi puoi lanciare i valori.

+0

Ho riconosciuto nel mio post di apertura che il compilatore dice che sono puntatori non correlati. Quello che voglio sapere è _why_. Mi sembra che se 'T1' è" correlato "a' T2', quindi 'T1 *' dovrebbe essere "correlato" a 'T2 *'. Perché non è il suono della regola di digitazione (per i tipi primitivi)? – Nick

+3

@Nick non è "in qualche modo correlato" ma "classi correlate". Come hai detto, i caratteri char e unsigned sono primitivi, non classi. Questa è la ragione - ed è quello che ho detto se leggi attentamente. Hai ragione - se la classe T1 è legata alla classe T2, allora puoi usare static_cast per convertire T1 * in T2 *. Questo non è quello che stai facendo. char non è correlato al char non firmato nel senso della relazione richiesta dallo standard C++. –

+0

Tuttavia, se si rilascia il puntatore, il compilatore non ha problemi di trasmissione tra tutti i tipi primitivi, ad es. 'unsigned char a = 255; char b = static_cast (a); 'Sembra un po 'strano, dal momento che se' T1' e 'T2' sono classi, il cast tra puntatori non è corretto, dal momento che si potrebbe fare qualcosa del tipo: ' classe A; ' classe 'B: un pubblico;' 'B * b = new B [4];' 'b [0] = B();' 'a * a = static_cast (b);' 'un [1] = A(); ' ' B b1 = b [1]; // oops' Sembra che la _only_ volta che il cast sia sicuro è tra i tipi primitivi. – Nick

4

Oltre ad essere puntatori, unsigned char * e char * hanno nulla in comune (EdChum già menzionato il fatto che char, signed char e unsigned char sono tre tipi diversi). Si potrebbe dire la stessa cosa per i tipi di puntatore Foo * e Bar * a qualsiasi struttura dissimile.

static_cast significa che un puntatore del tipo di origine può essere utilizzato come puntatore del tipo di destinazione, che richiede una relazione di sottotipo. Quindi non può essere usato nel contesto della tua domanda; quello che ti serve è o reinterpret_cast che fa esattamente quello che vuoi o un cast in stile C.