Domanda

Sto leggendo un libro e ho trovato che reinterpret_cast non deve essere utilizzato direttamente, ma piuttosto di casting a void* in combinazione con static_cast:

T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);

Invece di:

T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);

Tuttavia, non riesco a trovare una spiegazione del perché è meglio che la diretta cast.Sarei molto grato se qualcuno può darmi una spiegazione o mi punto per la risposta.

Grazie in anticipo

p.s.Io so cosa è reinterpret_cast utilizzato per, ma non ho mai visto che viene utilizzato in questo modo

È stato utile?

Soluzione

Per i tipi per i quali il cast è consentito (ad es.se T1 è un CONTENITORE di tipo e T2 è unsigned char), l'approccio con static_cast è ben definito dallo Standard.

D'altra parte, reinterpret_cast è interamente definito dall'implementazione - l'unica garanzia che si ottiene è che si può lanciare un tipo di puntatore a qualsiasi altro tipo di puntatore e poi di nuovo, e si otterrà il valore originale;e anche, si può lanciare un tipo di puntatore a un tipo integrale abbastanza grande per contenere un valore di puntatore (che varia a seconda dell'implementazione, e non deve esistere), e poi gettato indietro, e si otterrà il valore originale.

Per essere più specifici, mi limiterò a citare le parti pertinenti della Norma, evidenziando le parti più importanti:

5.2.10[expr.reinterpretare.cast]:

La mappatura eseguita da reinterpret_cast è definito dall'implementazione.[Nota:potrebbe, o non potrebbe, produrre una rappresentazione diversa dal valore originale.] ...Un puntatore a un oggetto possono essere convertiti in modo esplicito a un puntatore a un oggetto di tipo diverso.) Tranne che per la conversione di un rvalue di tipo “puntatore a T1” per il tipo “puntatore a T2” (dove T1 e T2 sono i tipi di oggetto e in cui i requisiti di allineamento di T2 non sono più severi rispetto a quelli di T1) e torna al suo tipo originale produce l'originale valore del puntatore, il risultato di tale puntatore di conversione non è specificato.

Quindi, qualcosa di simile a questo:

struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);

è, effettivamente, non specificato.

Spiegare il perché static_cast le opere è un po ' più difficile.Ecco il codice di cui sopra, riscritto per utilizzare static_cast e credo che questo sia garantita sempre funziona come previsto dalla Norma:

struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);

Di nuovo, mi si consenta di citare le sezioni della Norma che, insieme, mi portano a concludere che il portatile:

3.9[di base.tipi]:

Per qualsiasi oggetto (che non sia di base di classe secondario) di tipo T, se l'oggetto ha un valore di tipo T, il sottostante byte (1.7) costituenti l'oggetto può essere copiato in un array di char o unsigned char.Se il contenuto dell'array di char o unsigned char viene copiata in oggetto, l'oggetto, successivamente, di tenere il suo valore originale.

L'oggetto della rappresentazione di un oggetto di tipo T è la sequenza di N unsigned char oggetti preso da un oggetto di tipo T, dove N è uguale a sizeof(T).

3.9.2[di base.composto]:

Oggetti di cv qualificato (3.9.3) o il cv tipo non qualificato void* (un puntatore a void), può essere utilizzato per oggetti di tipo sconosciuto.Un void* deve essere in grado di contenere qualsiasi oggetto puntatore. Un cv qualificato o cv-non qualificato (3.9.3) void* deve avere la stessa rappresentazione e i requisiti di allineamento come un curriculum vitae-completo o cv-non qualificati char*.

3.10[di base.lval]:

Se un programma tenta di accedere al valore di un oggetto attraverso un lvalue di uno dei seguenti tipi di comportamento è indefinito):

  • ...
  • un char o unsigned char tipo.

4.10[conv.ptr]:

Un rvalue di tipo “puntatore cv T”), dove T è un tipo di oggetto, può essere convertito in un rvalue di tipo “puntatore cv vuoto”. Il risultato della conversione di un “puntatore cv T” per un “puntatore cv void” punti all'inizio del percorso di archiviazione in cui l'oggetto di tipo T risiede, come se l'oggetto è un oggetto derivato (1.8) di tipo T (che è, non una classe di base oggetto secondario).

5.2.9[expr.statico.cast]:

L'inversa di una conversione standard sequenza (clausola 4) oltre alla lvalue a rvalue (4.1), array-topointer (4.2), la funzione di puntatore (4.3), e un valore booleano (4.12) conversioni, può essere eseguita in modo esplicito utilizzando static_cast.

[MODIFICA] D'altra parte, abbiamo questo gioiello:

9.2[classe.mem]/17:

Un puntatore a un POD-struttura in oggetto, opportunamente convertiti con un reinterpret_cast, punti iniziali gli stati (o se il membro è un campo di bit, quindi per l'unità in cui risiede) e viceversa.[Nota:Ci potrebbe quindi essere senza nome imbottitura all'interno di un CONTENITORE-struttura in oggetto, ma non al suo inizio, in quanto necessario per ottenere il giusto allineamento.]

il che sembra implicare che reinterpret_cast tra puntatori in qualche modo implica "stesso indirizzo".Va ' a sapere.

Altri suggerimenti

non è il minimo dubbio che l'intento è che entrambe le forme sono ben definiti, ma la formulazione non riesce a catturare questo.

Entrambe le forme lavoreranno in pratica.

reinterpret_cast è più esplicito circa l'intenzione e dovrebbe essere preferito.

La vera ragione questo è così è a causa di come C ++ definisce l'ereditarietà, ed a causa di puntatori membri.

Con C, puntatore è praticamente solo un indirizzo, come dovrebbe essere. In C ++ deve essere più complessa a causa di alcune delle sue caratteristiche.

puntatori membri sono davvero un offset in una classe, quindi lanciare loro è sempre un disastro con stile C.

Se avete moltiplicare ereditato due oggetti virtuali che hanno anche alcune parti in calcestruzzo, che è anche un disastro per stile C. Questo è il caso in ereditarietà multipla che causa tutti i problemi, però, quindi non dovrebbe mai voglia di utilizzare questo comunque.

In realtà si spera non si utilizza mai questi casi, in primo luogo. Inoltre, se si sta lanciando un sacco che è un altro segno che si sta rovinare in nel vostro progetto.

L'unica volta che finisco casting è con le primitive in aree C ++ decide non sono le stesse, ma in cui, ovviamente, devono essere. Per oggetti reali, ogni volta che si vuole lanciare qualcosa, inizia a mettere in discussione il vostro disegno, perché si dovrebbe essere 'di programmazione per l'interfaccia' la maggior parte del tempo. Naturalmente, non è possibile modificare il modo 3 ° API parti lavorare in modo non sempre hanno molta scelta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top