Newsgroup: it.comp.lang.c++
Titolo: quando si fa il cast esplicito?

fino a poco tempo fa mi ero abituato a fare sempre esplicitamente il
cast tra tipi, in qualunque situazione... senza lasciare che fosse il
compilatore a farlo implicitamente, in modo da evitare errori di
distrazione...
solo che mi chiedevo se questo è effettivamente un buon modo di
procedere... spesso mi trovo con lunghe liste di static_cast<>() e
forse posso lasciar fare al compilatore cast semplici, ad esempio, tra
int e short e viceversa, senza dirglielo esplicitamente, che ne dite?
-- 
]\/[ad]\/[age
"Un Maestro insegue semplicemente la sua visione di Eccellenza in
qualunque cosa sta facendo e lascia agli altri decidere se sta 
lavorando o giocando. A lui sembra sempre di fare entrambe." - Anonimo

Il cast č una operazione che puň fare tranquillamente il compilatore, sempre
che i tipi siano compatibili tra di loro.
Lascia fare sempre al compilatore!
Il cast esplicito, si utilizza eccezionalmente ed il piů delle volte quando
si ha a che fare con tipi progettati dall'utente, che perň devono essere
sempre compatibili tra di loro.
___________________________________________
Wrote Mario from: 
www.mp.uklinux.net

 sempre compatibili tra di loro.
Non l'ho mai fatto (né credo che abbia molto senso) ma,
definendo un operatore di conversione per il tuo tipo,
credo che sia possibile convertire pressochè qualunque classe
in qualunque altra anche se scorrelata.
--
Gerolamo
Using: Win2000 Pro, Borland C++ Compiler 5.5.1
Listening: Sting - Fragile

 in qualunque altra anche se scorrelata.
Non ho ben capito cosa intendi per operatore di conversione. Vuoi dire che se ho
due classi Alfa e Beta posso definire un operatore
Beta operator(Beta)(Alfa& a)?
Ci sarebbe un'utilita' particolare rispetto ad una normale funzione
Beta cast_to_Beta(Alfa& a)?
Grazie e ciao
---
Riccardo

 Beta cast_to_Beta(Alfa& a)?
mmm... non saprei... intanto pero' questa deve essere una funzione
dichiarata in uno scope comune ad alfa e beta, ma fuori da entrambe.
Con l'operatore invece rimango all'interno delle classi alfa e beta,
in teoria ottenendo codice piu' ordinato...
Di piu' non saprei dirti.
--
Gerolamo
Using: Win2000 Pro, Borland C++ Compiler 5.5.1
Listening: The Cure - Pictures of You

 in qualunque altra anche se scorrelata.
Si. Ma gli operatori di per se servono a permettere le operazioni.
Spesso con tipi definiti dall'utente che si convertono uno con l'altro
ci si trova con situazioni di conversione ambigue per cui serve
l'esplicitazione della coversione (vanno evitate anche queste, IMO, ma
non sempre è possibile e si rintra in uno di quei casi speciali che
dicevo prima).
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

 };
Tecnicamente anche questo è un operatore di conversione (da intero a
Alfa). Se vuoi evitare che funga da operatore di conversione ma continui
a essere un costruttore devi dichiararlo explicit.


 a essere un costruttore devi dichiararlo explicit.
Ah. Non lo sapevo.
Adesso mi vado a leggere che dice lo Stroustrup di explicit.
Grazie della dritta!
--
Gerolamo
Using: Win2000 Pro, Borland C++ Compiler 5.5.1
Listening: Sting - When we dance

 dicevo prima).
Non ho capito l'obbiezione.
Se sono conversioni tra tipi definiti da me e ho definito gli opportuni
operatori, mi basta esplicitare il cast e sono a posto.
Ma può darsi che mi sfugga qualcosa...
--
Gerolamo
Using: Win2000 Pro, Borland C++ Compiler 5.5.1
Listening: Sting - When we dance

comune che va documentata.
beh, il problema nasceva da questi due casi specifici:
cast da interi a stringa e viceversa (ovvio e semplice che non si può
evitare!!!)
cast da interi inutili a short... questo perché la libreria che uso
(VCL) restituisce praticamente sempre int, mentre per i miei scopi
(che ora è troppo lungo da spiegare qui) mi servono degli short
(fondamentalmente quando li vado a memorizzare su file)


 Ma può darsi che mi sfugga qualcosa...
Non avevi detto che eri d'accordo sul fatto che esplicitare i cast va
evitato il più possibile? Se non è possibile è perchè c'è un problema o
una decisione di design che lasciano aperte delle ambiguità. E in questo
caso bisogna identificare il problema o documentare queste decisioni. La
presenza del cast è proprio idice di questa situasione. Ma se espliciti
tutte le conversioni, ovviamente devi comunque distinguere quali sono
solo esplicitazioni e quali sono dovute a problemi o al design ... e
ritorniamo all'inizio del messaggio.


 ritorniamo all'inizio del messaggio.
Allora proviamo con questa linea di pensiero:
1- Le conversioni automatiche tra tipi primitivi non vanno in
    generale esplicitate.
2- Le conversioni tra classi e/o tipi definiti dall'utente vanno in generale
    esplicitate e documentate.
(mi sento che manca un punto 3 :-)))
Puo' andare secondo te?


 (fondamentalmente quando li vado a memorizzare su file)
Questa è una vera conversione. Il fatto che non lo spieghi sul NG è Ok.
Ma devi spiegare nella documentazione il perchè usi degli short e il
perchè usi la VCL che invece restituisce int. Infine per tutti i casi in
cui la cosa non è ovvia devi dimostrare e spiegare il perchè l'int
restituito dalla VCL ha CERTAMENTE un valore che può stare comodamente
in uno short e laddove questo non sia provato a priori, dovresti
inserire un test che lo verifica a runtime (e che fa qualche cosa di
sensato se il valore è troppo alto). In alternativa puoi anche
considerare la cosa in termini di eccezione e scrivere delle apposite
macro e routine di conversione fatte in questo modo (o molto simili):
#define ToShort( Exp ) ExpToShort( #Exp, Exp )
#ifdef DEBUG
  #define IsShort( Exp ) ToShort( Exp )
#else
  #define IsShort( Exp ) short( Exp )
#endif DEBUG
template <class V> inline short ExpToShort( const char*exp, V v )
{
    if ( v < std::numeric_limits<short>::min() ||
         v > std::numeric_limits<short>::max() )
        throw std::range_error(exp);
    return short(v);
}
dove ovviamente IsShort è da usare quando pensi che sia garantito che il
valore ritornato dall'espressione non possa essere mai troppo grosso
mentre ToShort è da usare quando questa certezza non esiste (o meglio
quando non è matematica ma dipende da variabili che dovrebbero essere
corrette ma che non puoi verificare se non a runtime).


 Puo' andare secondo te?
Io direi così (nota: un "cast" è sempre esplicito. Con "cast" intendo
una conversione esplicita.):
1) in generale meglio evitare i cast (vale anche per tipi definiti
dall'utente)
2) quando un cast si rende necessario, documentare il motivo per cui non
era evitabile
3) in generale un cast può essere necessario (elenco i casi che mi
vengono in mente):
-) in caso di ambiguità nella conversione (documentare il perchè
entrambe le funzioni che creano ambiguità ovvero entrambe le conversioni
coinvolte sono necessarie)
-) in caso non vi sia ambiguità ma si voglia utilizzare un overload
diverso da quello scelto dal linguaggio (documentare, ovviamente, il
perchè i due overload non sono equivalenti ed in particolare perchè
quello scelto dal linguaggio non va bene).
-) in caso di conversione da un tipo (per esempio aritmetico) a un'altro
più restrittivo (documentare il perchè la cosa è necessarioa e perchè si
è certi che non vi sia overflow, ovvero verificare esplicitamente il
caso di overflow)


 int e short e viceversa, senza dirglielo esplicitamente, che ne dite?
Intanto il linguaggio predefinisce un insieme di conversioni sicure,
dove il cast e' inutile, ovvero tutte quelle che partono da un tipo
numerico con rappresentazione piu' piccola ad un valore numerico con
rappresentazione piu' grande.
Per le conversioni insicure comunque il cast (*) non ti garantisce la
correttezza della conversione, ma puo' servire per zittire i warning del
compilatore. Un'alternativa interessante e' usare il numeric_cast delle
librerie Boost, che invece fa il controllo.
Invece per le conversioni di puntatori e reference che percorrono una
gerarchia di eredita' il dynamic_cast e' indispensabile, salvo per le
conversioni dirette derivata -> base (in alcuni casi puo' funzionare
anche lo static_cast, ma e' pericoloso, perche' con qualche cambiamento
il codice si puo' rompere silenziosamente).
reinterpret_cast andrebbe usato solo in casi estremi e in codice di
basso livello.
Ciao,
Nicola
(*) static_cast, perche il cast stile C non va mai usato.

 vengono in mente):
Ovviamente nel messaggio precedente non tenevo conto dei cast fra
puntatori e reference.
Per questi la regola in genere è:
-) non fare downcast espliciti se non in caso di ambiguità (caso con
ereditarietà multipla).
-) eventuali upcast solo con dynamic_cast e RTTI abilitato, solo se
necessari (spesso sono evitabili con una virtuale) e se proprio proprio
il cast dinamico da problemi di performance documentare le misurazioni
che portano a questa conclusione e il perchè un cast non dinamico è
certamente valido.
-) i reinterpret_cast e i const_cast andrebbero evitati come la morte se
non in casi veramente rari e perfettamente documentati (e solo in codice
che implementa un basso livello di astrazione).


"Andrea Ferro" <And...@UrkaDVD.it> ha scritto nel messaggio
news:Cj6L7.26$vs6....@nreader3.kpnqwest.net...
[cut]
ok. Faccio tesoro degli ultimi due messaggi
(e di quello di Nicola Musatti)
Quando si dice che l'esperienza insegna...
--
Gerolamo

(*) static_cast, perche il cast stile C non va mai usato.
no? perché no?


 no? perché no?
Guarda la differenza:
int pippo, pluto;
double res  ((double) pippo ) / pluto;
e questa:
int pippo, pluto;
double res  static_cast<double>(pippo ) / pluto;
quale risulta il piu' legibile ?
Poi alla C puoi inavvertitamente togliere una constness
mentre con lo static_cast non puoi visto che per farlo
devi usare il const_cast.
Cao
Gaetano.
-- 
Posted from 
dell-01.mm.eutelsat.org
 [194.214.173.17] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

quale risulta il piu' legibile ?
e se la scrivo cosě:
double res = double(pippo) / pluto;
come si faceva il cast con le prime versioni del c++?
approvo l'idea di togliere inavvertitamente un const...


 approvo l'idea di togliere inavvertitamente un const...
Il vero motivo non risiede in una questione di leggibilità ma nel fatto
che il cast "C-like" è pericoloso in quanto racchiude in una sintassi
unica vari tipi di cast. E non è detto che applichi sempre quello
giusto.
In pratica si comporta come uno static_cast. Salvo che accetta di
modificare la "constness" come un const cast, accetta le conversioni
tipiche del reinterpret_cast e, in caso di downcast di gerarchie, non è
chiaro che la conversione è statica o dinamica.
Personalmente ritengo che usare i cast C++ sia meglio anche se per
codice "usa e getta" io continuo a usare il vecchio cast C-like in molte
situazioni. Questa mia pratica è un difetto. Come quelo di non
sviluppare fino in fondo il design e di non scrivere adeguata
documentazione. Nel caso specifico di codice "usa e getta" la cosa resta
comunque un grosso difetto. Salvo che, dato che il codice non sopravvive
che per poche ore o giorni e solo sulla mia macchina, nessuno arriva a
rimproverarmi (salvo me stesso quando faccio pubblicamente ammenda
autodenunciandomi come sto facendo ora).


 scrisse:
[snip]
Io uso queste macro
/* macro.h */
/* altre utilities */
#ifdef __cplusplus
# define const_cast(T, e) const_cast<T>(e)
# define dynamic_cast(T, e) dynamic_cast<T>(e)
# define reinterpret_cast(T, e) reinterpret_cast<T>(e)
# define static_cast(T, e) static_cast<T>(e)
#else
# define const_cast(T, e) ((T) (e))
/* indeciso se e' utile in C */
# define dynamic_cast(T, e) ((T) (e))
# define reinterpret_cast(T, e) ((T) (e))
# define static_cast(T, e) ((T) (e))
#endif
/* code.c */
int pippo, pluto;
double res = static_cast(double, pippo ) / pluto;
-- 
+---- | Un OS per domarli, Un OS per trovarli,             |  -o)
+---+ | Un OS per ghermirli e nel buio incatenarli         |  /\\
 O  | | Nella terra di Redmond, ove l'ombra cupa scende... | _\_V
                    raffaele punto salmaso presso libero punto it

 #endif
Mah, io mi abituerei a scrivere il C in C ed il C++ in C++.
Ciao,
Nicola

Mah, io mi abituerei a scrivere il C in C ed il C++ in C++.
Con header che devono essere inclusi sia in progetti C che in C++?
Cosi' mi sembra piu' comodo (sceglie il compilatore!), e anche
uniforme.
[ok, il preprocessore in base al linguaggio]


Mah, io mi abituerei a scrivere il C in C ed il C++ in C++.
Concordo. E poi le macro possono a volte essere molto, molto
pericolose.
Il Ghenio
---------
These are my parting words to you:
those who give up are doomed!

 uniforme.
Ma questi header contengono codice? Anche questo mi sembrerebbe
pericoloso.
Ciao,
Nicola

situazioni.
Io lo uso quando porgrammo in windows usando le api (e uso anche
NULL), anche in questo caso è sbagliato farlo?
Continuo a farlo sia perché la documentazione per la api è per il C
sia perché i cast sono molto frequenti e in questo caso trovo più
leggibile i cast C-like.
-- 
                    Marco Barisione (ICQ 84277092)
Se sei interessato alla creazione di un newsgroup generico in cui si
possa parlare di quei linguaggi di programmazione senza un gruppo
apposito vota per it.comp.lang su it.news.annunci.

 NULL), anche in questo caso č sbagliato farlo?
Non č mai sbagliato, se lo si fa correttamente. Ma č piů facile non
farlo correttamente quando si usa la sintassi C. Per questo la sintassi
C++ č sempre preferibile pur rimanendo vero che la sintassi C non č
sbagliata.


 lavorando o giocando. A lui sembra sempre di fare entrambe." - Anonimo
I cast espliciti dovrebbero essere il piů possibile evitati; lo Stroustrup
nel suo libro afferma che la notazione static_cast<>() č stata pensata per
essere la piů inelegante possibile proprio per scoraggiare questi cast,
quindi.... a buon intenditor poche parole. Le conversioni implicite, invece
avvengono normalmente in una espressione del tipo:
a+b  (con a intero e b short)
e in questo caso puoi benissimo lasciar fare al compilatore, stando perň
attentissimo a quello che fai!!!!!
Se usi le classi per definire i tuoi tipi astratti, puoi definire
addirittura un costruttore di conversione per creare le tue conversioni
implicite (quando il compilatore non puň applicare le conversioni implicite,
passa ai costruttori), percui concluderei affermando che non c'č niente di
male a lsaciar fare al compilatore (stando attenti ai tranelli, perň)

Occorre precisare una cosa.
Il cast non è un'operazione macchina ma un'istruzione al compilatore.
Ciò significa che ogni volta che si mette un cast, il compilatore
trasforma in istruzioni. Magari per nulla!
Poi magari compilatori particolarmente ottimizzati potrebbero in
alcune situazioni non fare il cast se questo fa una conversione tra
due tipi uguali anche dove chiesto però la cosa è pericolosa per cui,
secondo me, se si chiede il cast il compilatore lo fa in ogni caso.
Seconda cosa e qui arrivano le note dolenti.
Come tutti qui sappiamo, ogni processore privileggia le operazioni
fatte su un tipo di dato che è quello nativo della macchina. Il
compilatore (o meglio, il buon compilatore) associa questo tipo
all'int.
In sostanza, una somma tra int è più efficiente di una somma tra due
tipi diversi, qualunque essi siano ed indipendentemente dal numero di
bit coinvolti.
Obbligare il processore a lavorare su uno short, per esempio,
significa farlo lavorare più lentamente ed inoltre aggiungere il
codice macchina per fare il cast. Beffati due volte, quindi.
In sostanza: quando non si ha motivo di volere un tipo, usare sempre
il generico int senza fare alcun cast. Il compilatore in queste
condizioni userà il codice migliore ed il tipo dato migliore per
quella specifica macchina target.
Allora il cast dove lo uso? Dove per questioni di target diversi ho il
sospetto che macchine diverse potrebbero usare lunghezze word diverse.
Il cast non deve essere visto come uno strumento per avere la
correzione automatica! Quest'uso si paga! E' preferibile imparare bene
le regole di conversione ed attenervisi.
Buona programmazione a tutti. :-)

ah, beh... se č cosě lascio perdere gli short... ma tu sei sicuro di
quello che dici?


 quello che dici?
un minimo di quoting no? Ho dovuto attivare la modalità che visualizza
tutti i messaggi per capire che replicavi al messaggio di Francesco De
Francesco.
Effettivamente il post del Francesco mi sembra abbastanza confuso e non
del tutto corretto. Comunque nel caso specifico di scelta fra int e
short sappi che:
L'aritmetica fra short non è MAI più efficiente di quella fra int. O
l'efficienza è la stessa o è minore.
Stesso dicasi per l'accesso alla memoria di uno short o di un int.
Come paramteri e come valori di ritorno, alcuni sistemi gestiscono gli
short come short ma la maggior parte dei sistemi quando dichiari uno
short come parametro di fatto passano un int (e poi accede solo alla
parte meno significativa). Naturalmente parlo a livello di codice
macchina. A livello di linguaggio uno short non è mai un int!
Invece all'interno di strutture o di array c'è una effettiva differenza.
Se accedere ad uno short in memoria può richiedere non meno tempo che
accedere ad un int (potrebbe richiederne di più) accedere ad un array
(nel senso di accedere singolarmente a tutti gli elementi dell'array in
un loop, non nel senso di accedere ad un singolo elemento) richiede
quasi certamente meno tempo che accedere ad un array di interi (salvo
che il numero di elementi sia lo stesso).
Pertanto ogni volta che hai a che fare con funzioni semplici, con
parametri, con valori di ritorno o con variabili automatiche, se usare
un int o uno short non fa differenza a livello logico è meglio usare un
int. Esempio classico una funzione che ritorna un codice di errore o
successo opure un indice usato in un for per accedere ad un array. Se
invece hai a che fare con strutture e soprattutto con array di dati,
allora short ti permette in generale di diminuire la dimensione totale
dell'insieme di dati. E per insiemi di dati della dimensione di alcuni
KByte questo fa quasi certamente la differenza anche in termini di
prestazioni (in un array di pochi elementi la cosa è ovviamente
ininfluente).


ininfluente).
il mio unico punto di partenza sull'usare gli short o meno è partito
anni fa, quando ho cominciato a scrivere funzioni sul salvataggio
delle stringhe, in cui per ottimizzare la lettura da file io scrivo
prima la lunghezza della stringa e poi recupero i char della stringa
tutti insieme...
e usavo gli short perché forse 255 caratteri per una stringa erano
pochi (usare un byte per la lunghezza) ma la lunghezza che mi
permetteva un int era decisamente eccessiva...
certo, stiamo parlando di 2 byte invece che 4, che è ovviamente
ininfluente, ma era l'idea che mi spingeva a pensarla così...
però se mi dite che per risparmiare due byte sul disco per ogni
stringa perdo in efficenza nel gestire quegli short (oltre al fatto
che gran parte delle librerie che uso (VCL) ritorna e vuole int e
quindi devo anche considerare continuamente il casting)... allora via
gli short e vai con gli int!


 trasforma in istruzioni. Magari per nulla!
non e' affatto detto; per alcuni tipi di cast puoi avere ragione (tipo 
da double a int o viceversa) ma per altri puo' non essere necessario 
alcun overhead (ad esempio un reinterpret_cast tra due tipi che hanno 
la stessa dimensione).
ciao Anto
-- 
Antonio Cuni, 19 anni, studente
Brainbench C++ Master: 4.6, higher than 98% of all previous test takers.
C Language: 3.32, higher than 79% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 gli short e vai con gli int!
Scusa ma non vedo il problema. Lo short sta sul disco. In memoria ci sta
la stringa che è zero terminated. Una funzione che ti calcola la
lunghezza della stringa tornerà size_t che a secondo del sistema sarà un
unsigned short, sunsigned int o unsigned long. Una volta scritta o letta
la stringa quel valore non è più ne short ne long. E' una stringa.


loro lunghezza media.
Io mi sono trovato recentemente a provare l'alternativa degli
interi compressi BER ... 7 bit utili per ogni carattere con
l'ultimo bit che indica che il numero e' finito.
Parlando di unsigned i numeri 0..127 occupano un solo byte,
128..16383 ne occupano due e cosi' via.
Il codice di encoding/decoding resta abbastanza semplice e
efficiente; il formato e' inoltre indipendente dall'endian-ness
e dalla dimensione degli interi.
Per salvare numeri con segno io ho usato la tecnica di spostare
il segno nel bit meno significativo (x diventa 2*-x+1 se
negativo e 2*x se positivo).
Andrea