Condividi tramite


Conversioni standard

Il linguaggio C++ definisce le conversioni tra i relativi tipi fondamentali. Inoltre, definisce le conversioni per i tipi derivati puntatore, riferimento e puntatore a membro. Queste conversioni sono denominate conversioni standard.

In questa sezione vengono descritte le conversioni standard seguenti:

  • Promozioni a intero

  • Conversioni integrali

  • Conversioni a virgola mobile

  • Conversioni integrali e a virgola mobile

  • Conversioni aritmetiche

  • Conversioni puntatore

  • Conversioni riferimento

  • Conversioni puntatore a membro

    Nota

    I tipi definiti dall'utente possono specificare le proprie conversioni. La conversione dei tipi definiti dall'utente è descritta in Costruttori e conversioni.

Il codice seguente genera conversioni (in questo esempio, promozioni a intero):

long  long_num1, long_num2;
int   int_num;

// int_num promoted to type long prior to assignment.
long_num1 = int_num;

// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;

Il risultato di una conversione è un l-value solo se genera un tipo di riferimento. Ad esempio, una conversione definita dall'utente dichiarata come operator int&() restituisce un riferimento ed è un l-value. Tuttavia, una conversione dichiarata come operator int() restituisce un oggetto e non è un l-value.

Promozioni a intero

Gli oggetti di un tipo integrale possono essere convertiti in un altro tipo integrale più ampio, ovvero un tipo che può rappresentare un set di valori più grande. Questo tipo di conversione esteso è denominato promozione integrale. Con l'innalzamento di livello integrale, è possibile usare i tipi seguenti in un'espressione ovunque sia possibile usare un altro tipo integrale:

  • Oggetti, valori letterali e costanti di tipo char e short int

  • Tipi di enumerazione

  • int campi di bit

  • Enumeratori

Le promozioni C++ sono "con mantenimento del valore", perché il valore dopo la promozione è garantito che sia uguale al valore prima della promozione. Nelle promozioni con mantenimento dei valori, gli oggetti di tipi integrali più brevi (ad esempio campi di bit o oggetti di tipo ) vengono promossi al tipo charint se int possono rappresentare l'intervallo completo del tipo originale. Se int non può rappresentare l'intervallo completo di valori, l'oggetto viene alzato di livello al tipo unsigned int. Anche se questa strategia è uguale a quella usata dallo standard C, le conversioni con mantenimento dei valori non mantengono la "firma" dell'oggetto.

Le promozioni a mantenimento del valore e le promozioni che mantengono il segno producono, in genere, gli stessi risultati. Tuttavia, possono produrre risultati diversi se l'oggetto alzato di livello appare come:

  • Operando di /, /=%, %=, <, , <=, >o>=

    Questi operatori si basano sul segno per determinare il risultato. Le promozioni con mantenimento dei valori e mantenimento dei segni producono risultati diversi quando vengono applicati a questi operandi.

  • Operando sinistro di >> o >>=

    Questi operatori gestiscono le quantità firmate e non firmate in modo diverso in un'operazione di spostamento. Per le quantità firmate, un'operazione di spostamento a destra propaga il bit di segno nelle posizioni di bit liberate, mentre le posizioni dei bit liberate sono riempite in quantità senza segno.

  • Argomento di una funzione di overload o dell'operando di un operatore di overload, che dipende dalla firma del tipo operando per la corrispondenza degli argomenti. Per altre informazioni sulla definizione di operatori di overload, vedere Operatori di overload.

Conversioni integrali

Le conversioni integrali sono conversioni tra tipi integrali . I tipi integrali sono char, short (o short int), int, longe long long. Questi tipi possono essere qualificati con signed o unsignede unsigned possono essere usati come abbreviati per unsigned int.

Signed to unsigned

Gli oggetti di tipi integrali con segno possono essere convertiti nei corrispondenti tipi senza segno. Quando si verificano queste conversioni, il modello di bit effettivo non cambia. Tuttavia, l'interpretazione dei dati cambia. Si consideri il codice seguente:

#include <iostream>

using namespace std;
int main()
{
    short  i = -3;
    unsigned short u;

    cout << (u = i) << "\n";
}
// Output: 65533

Nell'esempio precedente, un signed shortoggetto , iviene definito e inizializzato in un numero negativo. L'espressione (u = i) causa i la conversione in un oggetto unsigned short prima dell'assegnazione in u.

Senza segno a firmato

Gli oggetti dei tipi integrali senza segno possono essere convertiti nei tipi con segno corrispondenti. Tuttavia, se il valore senza segno non è compreso nell'intervallo rappresentabile del tipo con segno, il risultato non avrà il valore corretto, come illustrato nell'esempio seguente:

#include <iostream>

using namespace std;
int main()
{
short  i;
unsigned short u = 65533;

cout << (i = u) << "\n";
}
//Output: -3

Nell'esempio precedente è u un unsigned short oggetto integrale che deve essere convertito in una quantità firmata per valutare l'espressione (i = u). Poiché il valore non può essere rappresentato correttamente in un signed shortoggetto , i dati vengono interpretati erroneamente come illustrato.

Conversioni del punto a virgola mobile

Un oggetto di un tipo mobile può essere convertito in modo sicuro in un tipo mobile più preciso, ovvero la conversione non causa alcuna perdita di significato. Ad esempio, le conversioni da float a double o da double a long double sono sicure e il valore rimane invariato.

Un oggetto di un tipo mobile può anche essere convertito in un tipo meno preciso, se si trova in un intervallo rappresentabile da tale tipo. (Vedere Limiti mobili per gli intervalli di tipi mobili. Se il valore originale non è rappresentabile con precisione, può essere convertito nel successivo valore più alto o inferiore successivo rappresentabile. Il risultato non è definito se non esiste alcun valore di questo tipo. Si consideri l'esempio seguente:

cout << (float)1E300 << endl;

Il valore massimo rappresentabile per tipo float è 3.402823466E38 che è un numero molto più piccolo di 1E300. Di conseguenza, il numero viene convertito in infinito e il risultato è "inf".

Conversioni tra tipi integrali e tipi punto a virgola mobile

Alcune espressioni possono causare la conversione degli oggetti di tipo a virgola mobile in tipi integrali o viceversa. Quando un oggetto di tipo integrale viene convertito in un tipo mobile e il valore originale non è rappresentabile esattamente, il risultato è il successivo valore più alto o il successivo valore rappresentabile inferiore.

Quando un oggetto di tipo mobile viene convertito in un tipo integrale, la parte frazionaria viene troncata o arrotondata verso zero. Un numero come 1.3 viene convertito in 1 e -1.3 viene convertito in -1. Se il valore troncato è superiore al valore rappresentabile più alto o inferiore al valore rappresentabile più basso, il risultato non è definito.

Conversioni aritmetiche

Molti operatori binari (descritti in Espressioni con operatori binari) causano conversioni di operandi e producono risultati allo stesso modo. Le conversioni causate da questi operatori sono chiamate conversioni aritmetiche consuete. Le conversioni aritmetiche di operandi con tipi nativi diversi vengono eseguite come illustrato nella tabella seguente. I tipi typedef si comportano in base ai tipi nativi sottostanti.

Condizioni per la conversione dei tipi

Condizioni soddisfatte Conversione
Uno degli operandi è di tipo long double. L'altro operando viene convertito in tipo long double.
Condizione precedente non soddisfatta e nessuno degli operandi è di tipo double. L'altro operando viene convertito in tipo double.
Le condizioni precedenti non sono soddisfatte e nessuno degli operandi è di tipo float. L'altro operando viene convertito in tipo float.
Le condizioni precedenti non sono soddisfatte (nessuno degli operandi sono di tipo a virgola mobile). Gli operandi ottengono promozioni integrali come indicato di seguito:

- Se uno degli operandi è di tipo unsigned long, l'altro operando viene convertito in tipo unsigned long.
- Se la condizione precedente non viene soddisfatta e se uno degli operandi è di tipo long e l'altro di tipo unsigned int, entrambi gli operandi vengono convertiti in tipo unsigned long.
- Se le due condizioni precedenti non vengono soddisfatte e se uno degli operandi è di tipo long, l'altro operando viene convertito in tipo long.
- Se le tre condizioni precedenti non vengono soddisfatte e se uno degli operandi è di tipo unsigned int, l'altro operando viene convertito in tipo unsigned int.
- Se nessuna delle condizioni precedenti viene soddisfatta, entrambi gli operandi vengono convertiti in tipo int.

Il codice seguente illustra le regole di conversione descritte nella tabella:

double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}

La prima istruzione nell'esempio precedente mostra una moltiplicazione di due tipi integrali, iVal e ulVal. La condizione soddisfatta è che nessuno degli operandi è di tipo mobile e un operando è di tipo unsigned int. Quindi, l'altro operando, iVal, viene convertito in tipo unsigned int. Il risultato viene quindi assegnato a dVal. La condizione soddisfatta qui è che un operando è di tipo double, quindi il unsigned int risultato della moltiplicazione viene convertito in tipo double.

La seconda istruzione nell'esempio precedente mostra l'aggiunta di un float e un tipo integrale: fVal e ulVal. La ulVal variabile viene convertita in tipo float (terza condizione nella tabella). Il risultato dell'addizione viene convertito in tipo double (seconda condizione nella tabella) e assegnato a dVal.

Conversioni puntatore

I puntatori possono essere convertiti durante l'assegnazione, l'inizializzazione, il confronto e altre espressioni.

Puntatore a classi

Esistono due casi in cui un puntatore a una classe può essere convertito in un puntatore a una classe base.

Il primo caso è quando la classe base specificata è accessibile e la conversione non è ambigua. Per altre informazioni sui riferimenti ambigui alla classe base, vedere Più classi di base.

L'accessibilità di una classe base dipende dal tipo di ereditarietà utilizzato nella derivazione. Si consideri l'ereditarietà illustrata nella figura seguente:

Diagram showing an inheritance graph and base class accessibility.

Il diagramma mostra la classe di base A. La classe B eredita da A tramite pubblico protetto privato. La classe C eredita da B tramite b pubblico.

Grafico dell'ereditarietà che illustra l'accessibilità della classe base

Nella tabella seguente viene illustrata l'accessibilità della classe base per la situazione illustrata nella figura.

Tipo di funzione Derivazione Conversione da

B* per A* legale?
Funzione esterna (non a livello dell'ambito della classe) Privata No
Protetto No
Pubblico
Funzione del membro B (nell'ambito B) Privata
Protetto
Pubblico
Funzione del membro C (nell'ambito C) Privata No
Protetto
Pubblico

Il secondo caso in cui un puntatore a una classe può essere convertito in un puntatore a una classe base è quando si usa una conversione di tipo esplicita. Per altre informazioni sulle conversioni esplicite dei tipi, vedere Operatore di conversione di tipi espliciti.

Il risultato di tale conversione è un puntatore al sottooggetto, la parte dell'oggetto completamente descritta dalla classe di base.

Nel codice seguente vengono definite due classi, A e B, dove B è derivata da A. Per altre informazioni sull'ereditarietà, vedere Classi derivate. Definisce quindi bObject, un oggetto di tipo Be due puntatori (pA e pB) che puntano all'oggetto .

// C2039 expected
class A
{
public:
    int AComponent;
    int AMemberFunc();
};

class B : public A
{
public:
    int BComponent;
    int BMemberFunc();
};
int main()
{
   B bObject;
   A *pA = &bObject;
   B *pB = &bObject;

   pA->AMemberFunc();   // OK in class A
   pB->AMemberFunc();   // OK: inherited from class A
   pA->BMemberFunc();   // Error: not in class A
}

Il puntatore pA è di tipo A *, che può essere interpretato come "puntatore a un oggetto di tipo A". I membri di bObject (ad esempio BComponent e BMemberFunc) sono univoci per il tipo B e pertanto sono inaccessibili tramite pA. Il puntatore pA consente l'accesso solo a quelle caratteristiche (funzioni membro e dati) dell'oggetto definite nella classe A.

Puntatore a funzione

Un puntatore a una funzione può essere convertito in tipo void *, se il tipo void * è sufficientemente grande da contenere tale puntatore.

Puntatore a void

I puntatori al tipo void possono essere convertiti in puntatori a qualsiasi altro tipo, ma solo con un cast di tipo esplicito (a differenza di C). Un puntatore a qualsiasi tipo può essere convertito in modo implicito in un puntatore al tipo void. Un puntatore a un oggetto incompleto di un tipo può essere convertito in un puntatore a void (in modo implicito) e indietro (in modo esplicito). Il risultato di tale conversione è uguale al valore del puntatore originale. Un oggetto viene considerato incompleto se è dichiarato, ma sono disponibili informazioni insufficienti per determinarne le dimensioni o la classe di base.

Puntatore a qualsiasi oggetto che non const è o volatile può essere convertito in modo implicito in un puntatore di tipo void *.

Puntatori const e volatile

C++ non fornisce una conversione standard da un const tipo o volatile a un tipo diverso const da o volatile. Tuttavia, qualsiasi tipo di conversione può essere specificato utilizzando i cast di tipo espliciti (comprese le conversioni non sicure).

Nota

I puntatori C++ ai membri, ad eccezione dei puntatori ai membri statici, sono diversi dai puntatori normali e non hanno le stesse conversioni standard. I puntatori ai membri statici sono puntatori normali e hanno le stesse conversioni dei puntatori normali.

Conversioni di puntatori Null

Un'espressione costante integrale che restituisce zero o un'espressione di questo tipo viene convertita in un puntatore denominato puntatore Null. Questo puntatore confronta sempre un puntatore a un puntatore a qualsiasi oggetto o funzione valido. Un'eccezione è costituita da puntatori a oggetti basati, che possono avere lo stesso offset e puntare comunque a oggetti diversi.

In C++11 il tipo nullptr deve essere preferito al puntatore Null in stile C.

Conversioni di espressioni puntatore

Qualsiasi espressione con un tipo di matrice può essere convertita in un puntatore dello stesso tipo. Il risultato della conversione è un puntatore al primo elemento della matrice. Nell'esempio riportato di seguito viene illustrata tale conversione:

char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].

Un'espressione che dà come risultato una funzione che restituisce un tipo specifico viene convertita in un puntatore a una funzione che restituisce il tipo, salvo quando:

  • L'espressione viene usata come operando per l'operatore address-of (&).

  • L'espressione viene utilizzata come operando all'operatore function-call.

Conversioni riferimento

Un riferimento a una classe può essere convertito in un riferimento a una classe base in questi casi:

  • La classe base specificata è accessibile.

  • La conversione non è ambigua. Per altre informazioni sui riferimenti ambigui alla classe base, vedere Più classi di base.

Il risultato della conversione è un puntatore al sotto-oggetto che rappresenta la classe base.

Puntatore a membro

I puntatori a membri di classe possono essere convertiti durante l'assegnazione, l'inizializzazione, il confronto e altre espressioni. In questa sezione vengono descritte le seguenti conversioni puntatore-a-membro:

Puntatore a un membro della classe base

Un puntatore a un membro di una classe base può essere convertito in un puntatore a un membro di una classe derivata, quando sono soddisfatte le condizioni seguenti:

  • La conversione inversa, dal puntatore a una classe derivata al puntatore della classe base, è accessibile.

  • La classe derivata non eredita virtualmente dalla classe di base.

Se l'operando sinistro è un puntatore a un membro, l'operando destro deve essere del tipo di puntatore a membro oppure un'espressione costante che restituisce 0. Questa assegnazione è valida solo nei casi seguenti:

  • L'operando destro è un puntatore a un membro della stessa classe dell'operando sinistro.

  • L'operando sinistro è un puntatore a un membro di una classe derivata pubblicamente e inequivocabilmente derivante dalla classe dell'operando destro.

Puntatore Null alle conversioni dei membri

Un'espressione costante integrale che restituisce zero viene convertita in un puntatore Null. Questo puntatore confronta sempre un puntatore a un puntatore a qualsiasi oggetto o funzione valido. Un'eccezione è costituita da puntatori a oggetti basati, che possono avere lo stesso offset e puntare comunque a oggetti diversi.

Nel codice seguente viene illustrata la definizione di un puntatore al membro i nella classe A. Il puntatore pai viene inizializzato a 0, che è il puntatore null.

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

Vedi anche

Informazioni di riferimento sul linguaggio C++