8.5 — Conversione esplicita del tipo (casting) e static_cast

8.5 — Conversione esplicita del tipo (casting) e static_cast

Nella lezione 8.1 -- Conversione implicita del tipo (coercizione), abbiamo discusso del fatto che il compilatore può convertire implicitamente un valore da un tipo di dati a un altro attraverso un sistema chiamato implicit type conversion . Quando vuoi promuovere numericamente un valore da un tipo di dati a un tipo di dati più ampio, l'utilizzo della conversione implicita del tipo va bene.

Molti nuovi programmatori C++ provano qualcosa del genere:

double d = 10 / 4; // does integer division, initializes d with value 2.0

Perché 10 e 4 sono entrambi di tipo int , viene eseguita la divisione di interi e l'espressione restituisce int valore 2 . Questo valore subisce quindi la conversione numerica in double valore 2.0 prima di essere utilizzato per inizializzare la variabile d . Molto probabilmente, questo non è ciò che si intendeva.

Nel caso in cui si utilizzino operandi letterali, la sostituzione di uno o entrambi i valori letterali interi con valori letterali doppi causerà invece la divisione in virgola mobile:

double d = 10.0 / 4.0; // does floating point division, initializes d with value 2.5

Ma cosa succede se si utilizzano variabili invece di valori letterali? Considera questo caso:

int x { 10 };
int y { 4 };
double d = x / y; // does integer division, initializes d with value 2.0

Poiché qui viene utilizzata la divisione intera, la variabile d finirà con il valore di 2.0 . Come diciamo al compilatore che vogliamo usare la divisione in virgola mobile invece della divisione intera in questo caso? I suffissi letterali non possono essere utilizzati con le variabili. Abbiamo bisogno di un modo per convertire uno (o entrambi) degli operandi variabili in un tipo a virgola mobile, in modo che venga utilizzata invece la divisione in virgola mobile.

Fortunatamente, C++ viene fornito con diversi operatori di cast di tipi (più comunemente chiamati cast) che possono essere usati dal programmatore per richiedere che il compilatore esegua una conversione di tipo. Poiché i cast sono richieste esplicite da parte del programmatore, questa forma di conversione del tipo è spesso chiamata conversione esplicita del tipo (al contrario della conversione implicita del tipo, in cui il compilatore esegue automaticamente una conversione del tipo).

Digita casting

C++ supporta 5 diversi tipi di cast:C-style casts , static casts , const casts , dynamic casts e reinterpret casts . Gli ultimi quattro sono talvolta indicati come calchi con nome.

Tratteremo C-style casts e static casts in questa lezione.

Contenuti correlati

Discutiamo dei cast dinamici nella lezione 18.10 -- Cast dinamici, dopo aver trattato altri argomenti prerequisiti.

Const casts e reinterpret casts dovrebbero essere generalmente evitati perché sono utili solo in rari casi e possono essere dannosi se usati in modo errato.

Avvertimento

Evita i cast const e reinterpreta i cast a meno che tu non abbia una buona ragione per usarli.

Calchi in stile C

Nella programmazione C standard, i cast vengono eseguiti tramite l'operatore (), con il nome del tipo per convertire il valore posto tra parentesi. Potresti ancora vederli usati nel codice (o dai programmatori) che sono stati convertiti da C.

Ad esempio:

#include <iostream>

int main()
{
    int x { 10 };
    int y { 4 };

    
    double d { (double)x / y }; // convert x to a double so we get floating point division
    std::cout << d; // prints 2.5

    return 0;
}

Nel programma sopra, usiamo un cast in stile C per dire al compilatore di convertire x a un double . Poiché l'operando sinistro di operator/ ora restituisce un valore in virgola mobile, anche l'operando destro verrà convertito in un valore in virgola mobile e la divisione verrà eseguita utilizzando la divisione in virgola mobile anziché la divisione intera!

C++ ti permetterà anche di usare un C-style cast con una sintassi più simile a una chiamata di funzione:

    double d { double(x) / y }; // convert x to a double so we get floating point division

Funziona in modo identico all'esempio precedente, ma ha il vantaggio di mettere tra parentesi il valore da convertire (rendendo più facile capire cosa viene convertito).

Sebbene sia un C-style cast sembra essere un unico cast, può effettivamente eseguire una varietà di conversioni diverse a seconda del contesto. Questo può includere un static cast , un const cast o un reinterpret cast (gli ultimi due dei quali abbiamo menzionato sopra dovresti evitare). Di conseguenza, C-style casts rischiano di essere utilizzati inavvertitamente in modo improprio e di non produrre il comportamento previsto, cosa che è facilmente evitabile utilizzando invece i cast C++.

Contenuti correlati

Se sei curioso, questo articolo contiene ulteriori informazioni su come funzionano effettivamente i calchi in stile C.

Best practice

Evita di usare calchi in stile C.

cast_statico

C++ introduce un operatore di cast chiamato static_cast, che può essere utilizzato per convertire un valore di un tipo in un valore di un altro tipo.

Hai già visto static_cast usato per convertire un char in un int in modo che std::cout lo stampi come un intero invece di un char :

#include <iostream>

int main()
{
    char c { 'a' };
    std::cout << c << ' ' << static_cast<int>(c) << '\n'; // prints a 97

    return 0;
}

Il static_cast accetta un'espressione come input e restituisce il valore valutato convertito nel tipo specificato all'interno delle parentesi angolari. static_cast è meglio usare per convertire un tipo fondamentale in un altro.

#include <iostream>

int main()
{
    int x { 10 };
    int y { 4 };

    // static cast x to a double so we get floating point division
    double d { static_cast<double>(x) / y };  
    std::cout << d; // prints 2.5

    return 0;
}

Il principale vantaggio di static_cast è che fornisce il controllo del tipo in fase di compilazione, rendendo più difficile commettere un errore involontario. static_cast è anche (intenzionalmente) meno potente di C-style casts , quindi non puoi rimuovere inavvertitamente const o fare altre cose che potresti non avere intenzione di fare.

Best practice

Favorisci static_cast quando devi convertire un valore da un tipo a un altro.

Utilizzo di static_cast per rendere esplicite le conversioni restringenti

I compilatori emettono spesso avvisi quando viene eseguita una conversione di tipo implicito potenzialmente non sicura (restringente). Ad esempio, considera il seguente programma:

int i { 48 };
char ch = i; // implicit narrowing conversion

Trasmettere un int (2 o 4 byte) in un char (1 byte) è potenzialmente pericoloso (poiché il compilatore non può dire se il valore intero supererà l'intervallo di char o meno), e quindi il compilatore in genere stamperà un avviso. Se usiamo l'inizializzazione dell'elenco, il compilatore darebbe un errore.

Per aggirare questo problema, possiamo usare un cast statico per convertire esplicitamente il nostro intero in un char :

int i { 48 };

// explicit conversion from int to char, so that a char is assigned to variable ch
char ch { static_cast<char>(i) };

Quando lo facciamo, diciamo esplicitamente al compilatore che questa conversione è prevista e ci assumiamo la responsabilità per le conseguenze (ad es. l'overflow dell'intervallo di un char se ciò accade). Dall'output di questo static_cast è di tipo char , l'inizializzazione della variabile ch non genera alcun tipo di mancata corrispondenza, e quindi nessun avviso o errore.

Ecco un altro esempio in cui il compilatore in genere si lamenta della conversione di un double a un int potrebbe causare la perdita di dati:

int i { 100 };
i = i / 2.5;

Per dire al compilatore che intendiamo esplicitamente farlo:

int i { 100 };
i = static_cast<int>(i / 2.5);

Tempo del quiz

Domanda n. 1

Qual è la differenza tra conversione di tipo implicita ed esplicita?

Mostra soluzione

La conversione implicita del tipo viene eseguita automaticamente ogni volta che è previsto un tipo di dati, ma viene fornito un tipo di dati diverso.
La conversione esplicita del tipo avviene quando il programmatore utilizza un cast di tipo per convertire in modo esplicito un valore da un tipo a un altro tipo.