Appendice A

Conversioni di tipo


Per conversione di tipo si intende una operazione volta a trasformare un valore di un certo tipo in un altro valore di altro tipo. Questa operazione e` molto comune in tutti i linguaggi, anche se spesso il programmatore non se ne rende conto; si pensi ad esempio ad una operazione aritmetica (somma, divisione...) applicata ad un operando di tipo int e uno di tipo float. Le operazioni aritmetiche sono definite su operandi dello stesso tipo e pertanto non e` possibile eseguire immediatamente l'operazione; si rende quindi necessario trasformare gli operandi in modo che assumano un tipo comune su cui e` possibile eseguire l'operazione. Quello che generalmente fa, nel nostro caso, un compilatore di un qualsiasi linguaggio e convertire il valore intero in un reale e poi eseguire la somma tra reali, restituendo un reale.
Non sempre comunque le conversioni di tipo sono decise dal compilatore, in alcuni linguaggi (C, C++, Turbo Pascal) le conversioni di tipo possono essere richieste anche dal programmatore, distinguendo quindi tra conversioni implicite e conversioni esplicite. Le prime (dette anche coercizioni) sono eseguite dal compilatore in modo del tutto trasparente al programmatore, mentre le seconde sono quelle richieste esplicitamente:

  int i = 5;
  float f = 0.0;
  double d = 1.0;
        
  d = f + i;
        
  d = (double)f + (double)i;        
  // questa riga si legge: d = ((double)f) + ((double)i)
L'esempio precedente mostra entrambi i casi.
Nel primo assegnamento, l'operazione di somma e` applicata ad un operando intero e uno di tipo float, per poter eseguire la somma il compilatore C++ prima converte i al tipo float, quindi esegue la somma (entrambi gli operandi hanno lo stesso tipo) e poi, poiche` la variabile d e` di tipo double, converte il risultato al tipo double e lo assegna alla variabile.
Nel secondo assegnamento, il programmatore richiede esplicitamente la conversione di entrambi gli operandi al tipo double prima di effettuare la somma e l'assegnamento (la conversione ha priorita` maggiore delle operazioni aritmetiche).
Una conversione di tipo esplicita viene richiesta con la sintassi
    ( < NuovoTipo > ) < Valore >

    // oppure

    < NuovoTipo > ( < Valore > )
        
    // ma questo metodo puo` essere utilizzato solo con
    // nomi semplici (ad esempio non funziona con char*)
NuovoTipo puo` essere una qualsiasi espressione di tipo, anche una che coinvolga tipi definiti dall'utente; ad esempio:
    int a = 5;
    float f = 2.2;

    (float) a
    // oppure...
    float (a)
        
    // se Persona e` un tipo definito
    // dal programmatore...
        
    (Persona) f
    // oppure...
    Persona (f)
Le conversioni tra tipi primitivi sono gia` predefinite nel linguaggio e possono essere utilizzate in qualsiasi momento, il compilatore comunque le utilizza solo se il tipo di destinazione e` compatibile con quello di origine (cioe` puo` rappresentare il valore originale). Le conversioni da e a un tipo definito dall'utente richiedono che il compilatore sia informato riguardo a come eseguire l'operazione.
Per convertire un tipo primitivo (float, int, unsigned int...) in uno definito dall'utente e` necessario che il tipo utente sia una classe (o una struttura) e che sia definito un costruttore (pubblico) che ha come unico argomento un parametro del tipo primitivo:
    class Test {
      private:
        float member;

      public:
        Test(int a);
    };

    Test::Test(int a) {
      member = (float) a;
    }
Il metodo va naturalmente bene anche quando il tipo di partenza e` un tipo definito dall'utente.
Per convertire invece un tipo utente ad un tipo primitivo e` necessario definire un operatore di conversione; con riferimento al precedente esempio, il metodo da seguire e` il seguente:
    Test::operator int() { return (int) member; }
Se cioe` si desidera poter convertire un tipo utente X in un tipo primitivo T bisogna definire un operatore con nome T:
    X::operator T() { /* codice operatore */ }
Si noti che non e` necessario indicare il tipo del valore restituito, e` indicato dal nome dell'operatore stesso.
C'e` un aspetto che bisogna sempre tener presente: quando si definisce un operatore di conversione, questo non e` disponibile solo al programmatore, ma anche al compilatore che potrebbe utilizzarlo senza dare alcun avviso.
Le tecniche appena viste funzionano anche per conversioni verso tipi definiti dal programmatore e in effetti per il compilatore sono praticamente equivalenti e non c'e` motivo per preferirne una all'altra; tuttavia, poiche` i tipi predefiniti non sono classi, non e` possibile utilizzare la tecnica del costruttore per trasformare un tipo utente in uno predefinito (ad esempio int o char * ).
Un altro fattore da tener presente, quando si parla di conversioni, e` che non sempre una conversione di tipo preserva il valore: ad esempio nella conversione da float a int in generale si riscontra una perdita di precisione, perche` la rappresentazione adottata per gli interi e` incompatibile con quella adottata per i reali. Da questo punto di vista si puo` distinguere tra conversione di tipo con perdita di informazione e conversione di tipo senza perdita di informazione. In particolare in quest'ultimo caso si parla di conversioni triviali e promozione di tipo.
Le conversioni triviali sono:

DA: A:
T T&
T& T
T[ ] T*
T(args) T (*) (args)
T const T
T volatile T
T* const T*
T* volatile T*

Per promozione di tipo si intende invece una convesione che trasforma un valore di un tipo in un valore di un tipo piu` "grande", ad esempio un int in un long int oppure in un float, un tipo cioe` il cui insieme di valori rappresentabili includa l'insieme dei valori del tipo di partenza.






C++, una panoramica sul linguaggio - © Copyright 1997, Paolo Marotta