Ricerca…


introduzione

Un'espressione può essere convertita o cast esplicita per digitare T usando dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> , o const_cast<T> , a seconda del tipo di cast desiderato.

C ++ supporta anche la notazione cast in stile funzione, T(expr) e la notazione cast in stile C, (T)expr .

Sintassi

  • identificatore di tipo semplice ( )
  • identificatore di tipo semplice ( elenco di espressioni )
  • semplice-tipo-identificatore braced-init-list
  • typename-specificifier ( )
  • typename-specificifier ( expression-list )
  • typename-specifier braced-init-list
  • dynamic_cast < id-tipo > ( espressione )
  • static_cast < id-tipo > ( espressione )
  • reinterpret_cast < id-tipo > ( espressione )
  • const_cast < id-tipo > ( espressione )
  • ( type-id ) cast-expression

Osservazioni

Tutte e sei le annotazioni sul cast hanno una cosa in comune:

  • Il casting su un tipo di riferimento lvalue, come in dynamic_cast<Derived&>(base) , produce un lvalue. Pertanto, quando si vuole fare qualcosa con lo stesso oggetto ma trattarlo come un tipo diverso, si passa a un tipo di riferimento lvalue.
  • La trasmissione a un tipo di riferimento di rvalue, come in static_cast<string&&>(s) , produce un valore rvalue.
  • Il casting su un tipo non di riferimento, come in (int)x , produce un valore, che può essere considerato come una copia del valore da trasmettere, ma con un tipo diverso dall'originale.

La parola chiave reinterpret_cast è responsabile dell'esecuzione di due diversi tipi di conversioni "non sicure":

La parola chiave static_cast può eseguire una varietà di conversioni diverse:

  • Base per conversioni derivate

  • Qualsiasi conversione che può essere eseguita tramite un'inizializzazione diretta, incluse conversioni e conversioni implicite che chiamano una funzione di costruzione o conversione esplicita. Vedi qui e qui per maggiori dettagli.

  • Per void , che scarta il valore dell'espressione.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Tra i tipi aritmetici e di enumerazione e tra i diversi tipi di enumerazione. Vedi conversioni enum

  • Da puntatore a membro della classe derivata, puntatore al membro della classe base. I tipi a cui punta devono corrispondere. Vedi la conversione da derivata a base per i puntatori ai membri

  • void* a T* .

C ++ 11

Conversione da base a derivata

Un puntatore alla classe base può essere convertito in un puntatore alla classe derivata usando static_cast . static_cast non esegue alcun controllo di runtime e può portare a comportamenti non definiti quando il puntatore non punta effettivamente al tipo desiderato.

struct Base {};
struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1;                        // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
                                         // point to a Derived object

Allo stesso modo, un riferimento alla classe base può essere convertito in un riferimento alla classe derivata usando static_cast .

struct Base {};
struct Derived : Base {};
Derived d;
Base& r1 = d;
Derived& r2 = r1;                        // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object

Se il tipo di origine è polimorfico, dynamic_cast può essere utilizzato per eseguire una conversione da base a derivata. Esegue un controllo in fase di esecuzione e l'errore è recuperabile invece di produrre un comportamento non definito. Nel caso del puntatore, un puntatore nullo viene restituito in caso di errore. Nel caso di riferimento, viene generata un'eccezione in caso di errore di tipo std::bad_cast (o una classe derivata da std::bad_cast ).

struct Base { virtual ~Base(); }; // Base is polymorphic
struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Gettare via la costanza

Un puntatore a un oggetto const può essere convertito in un puntatore all'oggetto non-const usando la parola chiave const_cast . Qui usiamo const_cast per chiamare una funzione che non è const-correct. Accetta solo un argomento non-const char* anche se non scrive mai attraverso il puntatore:

void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s);                    // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*

const_cast al tipo di riferimento può essere utilizzato per convertire un valore lovalificato const-value in un valore non const-qualificato.

const_cast è pericoloso perché rende impossibile al sistema di tipo C ++ impedirti di tentare di modificare un oggetto const. Ciò comporta un comportamento indefinito.

const int x = 123;
int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Digitare la conversione punitiva

Un puntatore (riferimento) a un tipo di oggetto può essere convertito in un puntatore (riferimento) in qualsiasi altro tipo di oggetto utilizzando reinterpret_cast . Questo non chiama costruttori o funzioni di conversione.

int x = 42;
char* p = static_cast<char*>(&x);      // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z';                              // maybe this modifies x (see below)
C ++ 11

Il risultato di reinterpret_cast rappresenta lo stesso indirizzo dell'operando, a condizione che l'indirizzo sia allineato in modo appropriato per il tipo di destinazione. Altrimenti, il risultato non è specificato.

int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11

Il risultato di reinterpret_cast non è specificato, tranne che un puntatore (riferimento) sopravviverà a un round trip dal tipo di origine al tipo di destinazione e viceversa, purché il requisito di allineamento del tipo di destinazione non sia più rigido di quello del tipo di origine.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

Sulla maggior parte delle implementazioni, reinterpret_cast non cambia l'indirizzo, ma questo requisito non è stato standardizzato fino a C ++ 11.

reinterpret_cast può anche essere utilizzato per convertire da un tipo di membro puntatore a membro di dati a un altro o un tipo di funzione da puntatore a membro a un altro.

L'uso di reinterpret_cast è considerato pericoloso perché la lettura o la scrittura attraverso un puntatore o un riferimento ottenuto utilizzando reinterpret_cast può attivare un comportamento indefinito quando i tipi di origine e destinazione non sono correlati.

Conversione tra puntatore e intero

Un puntatore oggetto (incluso void* ) o un puntatore funzione può essere convertito in un tipo intero usando reinterpret_cast . Questo verrà compilato solo se il tipo di destinazione è abbastanza lungo. Il risultato è definito dall'implementazione e tipicamente restituisce l'indirizzo numerico del byte in memoria a cui puntano i puntatori del puntatore.

In genere, long o unsigned long è abbastanza lungo da contenere qualsiasi valore del puntatore, ma questo non è garantito dallo standard.

C ++ 11

Se i tipi std::intptr_t e std::uintptr_t esistono, sono garantiti per essere abbastanza lunghi da contenere un void* (e quindi qualsiasi puntatore al tipo di oggetto). Tuttavia, non è garantito che siano sufficientemente lunghi da contenere un puntatore a funzione.

Allo stesso modo, reinterpret_cast può essere utilizzato per convertire un tipo intero in un tipo di puntatore. Anche in questo caso il risultato è definito dall'implementazione, ma è garantito che il valore di un puntatore non venga modificato da un round trip attraverso un tipo intero. Lo standard non garantisce che il valore zero venga convertito in un puntatore nullo.

void register_callback(void (*fp)(void*), void* arg); // probably a C API
void my_callback(void* x) {
    std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
                  reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Conversione tramite costruttore esplicito o funzione di conversione esplicita

Una conversione che implica il richiamo di una funzione di costruzione o conversione esplicita non può essere eseguita implicitamente. Possiamo richiedere che la conversione venga eseguita esplicitamente utilizzando static_cast . Il significato è lo stesso di quello di un'inizializzazione diretta, tranne per il fatto che il risultato è temporaneo.

class C {
    std::unique_ptr<int> p;
  public:
    explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
    f(p);                 // error: C::C(int*) is explicit
    f(static_cast<C>(p)); // ok
    f(C(p));              // equivalent to previous line
    C c(p); f(c);         // error: C is not copyable
}

Conversione implicita

static_cast può eseguire qualsiasi conversione implicita. Questo uso di static_cast può talvolta essere utile, come nei seguenti esempi:

  • Quando si passano argomenti a puntini di sospensione, il tipo di argomento "previsto" non è noto staticamente, quindi non si verificherà alcuna conversione implicita.

    const double x = 3.14;
    printf("%d\n", static_cast<int>(x)); // prints 3
    // printf("%d\n", x); // undefined behaviour; printf is expecting an int here
    // alternative:
    // const int y = x; printf("%d\n", y);
    

    Senza la conversione di tipo esplicita, un double oggetto verrebbe passato ai puntini di sospensione e si verificherebbe un comportamento non definito.

  • Un operatore di assegnazione classe derivato può chiamare un operatore di assegnazione classe base in questo modo:

    struct Base { /* ... */ };
    struct Derived : Base {
        Derived& operator=(const Derived& other) {
            static_cast<Base&>(*this) = other;
            // alternative:
            // Base& this_base_ref = *this; this_base_ref = other;
        }
    };
    

Conversioni Enum

static_cast può convertire da un numero intero o un tipo a virgola mobile a un tipo di enumerazione (con ambito o senza ambito) e viceversa. Può anche convertire tra tipi di enumerazione.

  • La conversione da un tipo di enumerazione senza ambito a un tipo aritmetico è una conversione implicita; è possibile, ma non necessario, usare static_cast .
C ++ 11
  • Quando un tipo di enumerazione dell'ambito viene convertito in un tipo aritmetico:

    • Se il valore dell'enumerazione può essere rappresentato esattamente nel tipo di destinazione, il risultato è quel valore.
    • Altrimenti, se il tipo di destinazione è un tipo intero, il risultato non è specificato.
    • Altrimenti, se il tipo di destinazione è un tipo a virgola mobile, il risultato è uguale a quello della conversione al tipo sottostante e quindi al tipo a virgola mobile.

    Esempio:

    enum class Format {
        TEXT = 0,
        PDF = 1000,
        OTHER = 2000,
    };
    Format f = Format::PDF;
    int a = f;                         // error
    int b = static_cast<int>(f);       // ok; b is 1000
    char c = static_cast<char>(f);     // unspecified, if 1000 doesn't fit into char
    double d = static_cast<double>(f); // d is 1000.0... probably
    
  • Quando un numero intero o un tipo di enumerazione viene convertito in un tipo di enumerazione:

    • Se il valore originale è compreso nell'intervallo dell'enumerazione di destinazione, il risultato è tale valore. Si noti che questo valore potrebbe non essere uguale a tutti gli enumeratori.
    • In caso contrario, il risultato non è specificato (<= C ++ 14) o non definito (> = C ++ 17).

    Esempio:

    enum Scale {
        SINGLE = 1,
        DOUBLE = 2,
        QUAD = 4
    };
    Scale s1 = 1;                     // error
    Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
    Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
    Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
    
C ++ 11
  • Quando un tipo a virgola mobile viene convertito in un tipo di enumerazione, il risultato è lo stesso della conversione al tipo sottostante dell'enumerazione e quindi al tipo enum.

    enum Direction {
        UP = 0,
        LEFT = 1,
        DOWN = 2,
        RIGHT = 3,
    };
    Direction d = static_cast<Direction>(3.14); // d is RIGHT
    

Derivato per basare la conversione per i puntatori ai membri

Un puntatore al membro della classe derivata può essere convertito in un puntatore al membro della classe base utilizzando static_cast . I tipi a cui punta devono corrispondere.

Se l'operando è un puntatore nullo al valore del membro, il risultato è anche un puntatore nullo al valore del membro.

Altrimenti, la conversione è valida solo se il membro a cui punta l'operando esiste effettivamente nella classe di destinazione, o se la classe di destinazione è una classe di base o derivata della classe che contiene il membro puntato dall'operando. static_cast non verifica la validità. Se la conversione non è valida, il comportamento non è definito.

struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1;                              // ok; implicit conversion
int B::*p3 = p2;                              // error
int B::*p4 = static_cast<int B::*>(p2);       // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2);       // undefined; p2 points to x, which is a member
                                              // of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6);       // error: types don't match

void * a T *

In C ++, void* non può essere convertito implicitamente in T* dove T è un tipo di oggetto. Invece, static_cast dovrebbe essere usato per eseguire la conversione esplicitamente. Se l'operando punta effettivamente su un oggetto T , il risultato punta a quell'oggetto. Altrimenti, il risultato non è specificato.

C ++ 11

Anche se l'operando non punta a un oggetto T , finché l'operando punta a un byte il cui indirizzo è correttamente allineato per il tipo T , il risultato della conversione punta allo stesso byte.

// allocating an array of 100 ints, the hard way
int* a = malloc(100*sizeof(*a));                    // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100];                           // no cast needed
// std::vector<int> a(100);                         // better

const char c = '!';
const void* p1 = &c;
const char* p2 = p1;                           // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1);   // unspecified in C++03;
                                               // possibly unspecified in C++11 if
                                               // alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1);             // error: casting away constness

Casting in stile C.

Il cast di C-Style può essere considerato il cast "Best effort" e viene chiamato così come è l'unico cast che può essere usato in C. La sintassi per questo cast è (NewType)variable .

Ogni volta che questo cast viene usato, usa uno dei seguenti cast di c ++ (in ordine):

  • const_cast<NewType>(variable)
  • static_cast<NewType>(variable)
  • const_cast<NewType>(static_cast<const NewType>(variable))
  • reinterpret_cast<const NewType>(variable)
  • const_cast<NewType>(reinterpret_cast<const NewType>(variable))

Il casting funzionale è molto simile, anche se come alcune restrizioni come risultato della sua sintassi: NewType(expression) . Di conseguenza, è possibile trasmettere solo i tipi senza spazi.

È meglio usare il nuovo cast di c ++, perché è più leggibile e può essere facilmente individuato ovunque all'interno di un codice sorgente C ++ e gli errori verranno rilevati in fase di compilazione, invece in fase di esecuzione.

Poiché questo cast può risultare in reinterpret_cast non intenzionale, è spesso considerato pericoloso.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow