# Dal C al C++/Utilizzo basilare di librerie/L'uso dei template

Dal C al C++
Copertina Tutti i moduli · Sviluppo
* IntroduzioneDal C al C++/Introduzione
** I destinatari del libroDal C al C++/Introduzione#I destinatari del libro
** La struttura del libroDal C al C++/Introduzione#La struttura del libro
** Impieghi del linguaggioDal C al C++/Introduzione#Impieghi del linguaggio
** PanoramicaDal C al C++/Introduzione#Panoramica
* Utilizzo basilare di librerie
** ArrayDal C al C++/Array
** Il tipo "bool"Dal C al C++/Utilizzo basilare di librerie/Il tipo "bool"
** L'uso di classi e oggettiDal C al C++/Utilizzo basilare di librerie/L'uso di classi e oggetti
** Dichiarazioni e definizioniDal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni
*** Le variabili riferimentoDal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni#Le variabili riferimento
*** Il modificatore "const"Dal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni#Il modificatore "const"
*** Il sovraccaricamentoDal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni#Il sovraccaricamento
*** L'inizializzazione degli oggettiDal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni#L'inizializzazione degli oggetti
*** L'allocazione degli oggettiDal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni#L'allocazione degli oggetti
** L'uso dei templateDal C al C++/Utilizzo basilare di librerie/L'uso dei template
** L'uso dei namespaceDal C al C++/Utilizzo basilare di librerie/L'uso dei namespace
** I file di intestazioneDal C al C++/Utilizzo basilare di librerie/I file di intestazione
** L'uso delle stringheDal C al C++/Utilizzo basilare di librerie/L'uso delle stringhe
** I canali di ingresso/uscitaDal C al C++/Utilizzo basilare di librerie/I canali di ingresso/uscita
** La gestione delle eccezioni
** I contenitori standardDal C al C++/Utilizzo basilare di librerie/I contenitori standard
** Le funzioni standardDal C al C++/Utilizzo basilare di librerie/Le funzioni standard
* Utilizzo avanzato di librerie
** Ridefinizione di funzioni membroDal C al C++/Utilizzo avanzato di librerie/Ridefinizione di funzioni membro
** I parametri di defaultDal C al C++/Utilizzo avanzato di librerie/I parametri di default
** Gli iteratori sui contenitori
** Gli algoritmi sui contenitori
* Creazione di librerie
** Classi isolateDal C al C++/Classi isolate
*** I costruttoriDal C al C++/Classi isolate#I costruttori
*** I distruttoriDal C al C++/Classi isolate#I distruttori
*** Separazione tra interfaccia e implementazioneDal C al C++/Classi isolate#Separazione tra interfaccia e implementazione
** Gerarchie di classiDal C al C++/Gerarchie di classi
** Proprietari di risorse
** Eccezioni
** Namespace
** Template di funzione
** Template di classe
** Algoritmi su sequenze
* La metaprogrammazione
* La progettazione del software
* Gioco life di ConwayDal C al C++/Gioco life di Conway
Modifica il sommario
In C++ si possono definire non solo classi e funzioni, ma anche template di classe e template di funzione.
Per esempio, nella libreria standard è definito il template di classe "vector", che è un array dinamico. Tale template di classe non definisce il tipo degli elementi contenuti nell'array. Tale tipo è un parametro del template. Per poter utilizzare il template si deve specificare il tipo degli elementi. Vediamo qualche esempio:
La parola "vector" rappresenta la definizione di una classe generica, definita a meno di un parametro; non è una classe, ma una famiglia parametrica di classi, e quindi non si può usare direttamente per dichiarare o definire una variabile o una funzione.
Se dopo la parola "vector" si pone tra parentesi angolari un'espressione che identifica un tipo, si ottiene un'istanza del template, che è un tipo, utilizzabile come qualunque altro tipo.
L'istanziazione di un template di classe, cioè il passaggio dal template al tipo, viene eseguito in fase di compilazione, e quindi in fase di esecuzione non esistono affatto i template.
Un template può richiedere come parametro un valore intero, invece di un tipo. Consideriamo il seguente programma:
La prima riga della funzione "main" è la dichiarazione della variabile "a", di tipo "bitset<6>". Il template di classe "bitset" fa parte della libreria standard, ed è definito nel file di intestazione "bitset".
Questo template di classe rappresenta una sequenza di bit di lunghezza fissa, e richiede un parametro di tipo intero che indica il numero di bit contenuti nell'oggetto.
Nel nostro esempio, "a" è una sequenza di sei bit. L'oggetto "a" non è inizializzato esplicitamente, ma la classe bitset<6> lo inizializza implicitamente impostando a zero tutti i suoi bit.
Le due istruzioni successive impostano a 1 il primo e il terzo bit, contando da zero, cioè quelli di indice zero e di indice due.
L'ultima istruzione emette sulla console una rappresentazione testuale della variabile. Il risultato sarà la stampa della stringa "000101".
Chiaramente non si possono mescolare tipi con valori interi. Nella definizione di "vector" è specificato che il parametro deve essere un tipo, mentre nella definizione di "bitset" è specificato che il parametro deve essere un intero. Le seguenti due righe producono errori di compilazione:
Siccome l'istanziazione dei template avviene in fase di compilazione, è necessario che il valore intero usato come parametro di un template sia una costante calcolabile dal compilatore. Per esempio:
L'espressione usata per "b1" è ovviamente costante.
L'espressione usata per "b2" usa solo variabili "const", quindi il compilatore è in grado di valutare tale espressione.
L'espressione usata per "b3" somiglia alla precedente, ma usa la variabile "c3", che pur avendo già un valore in quel punto, è una variabile non "const" e quindi il suo valore non è costante, e il compilatore si rifiuta di usare tale valore contingente per istanziare un template.
In C++, oltre ai template di classe esistono i template di funzione. Ecco un esempio:
Questo codice utilizza due template di funzione: "max", che rende il valore maggiore tra i suoi due parametri, e "min", che rende il valore minore tra i suoi due parametri.
Il programma stampa su console la stringa "6.8 abc"; infatti, il numero "6.8" è maggiore del numero "2.3", e la stringa "abc" è minore della stringa "xyz" in ordine alfabetico.
In modo analogo ai template di classe, anche questi template di funzione sono stati istanziati specificando tra parentesi angolari il parametro del template, che per queste funzioni è il tipo dei parametri della funzione. Se si specifica un tipo e si passa un tipo diverso, si ottiene un errore sintattico. Per esempio:
Nella prima riga si passano due stringhe alla funzione "max<double>", che si attende due numeri; nella seconda riga si passano due numeri alla funzione "min<string>", che si attende due stringhe.
A differenza dei template di classe, i template di funzione possono sottintendere il parametro del template. Per esempio, il seguente programma è equivalente al precedente:
Siccome al template di funzione "max" vengono passati come parametri di funzione due valori entrambi di tipo "double", il compilatore deduce che il tipo del template è "double", e istanzia la funzione "max<double>". Analogamente, siccome al template di funzione "min" vengono passati come parametri di funzione due valori entrambi di tipo "string", il compilatore deduce che il tipo del template è "string", e istanzia la funzione "min<string>".
Tale inferenza automatica permette spesso al programmatore di scrivere meno codice. Tuttavia a volte l'inferenza non è possibile, in quanto c'è un'ambiguità. Per esempio:
In questa riga il compilatore non sa se istanziare la funzione "min<double>" o "min<int>" e quindi si arrende.
L'uso che viene fatto del valore reso da una funzione non viene mai utilizzato per inferire il parametro di un template, quindi il fatto che il valore sia usato per inizializzare una variabile di tipo "double" è ininfluente. Per risolvere l'ambiguità si può usare una delle tre righe seguenti:
La prima versione specifica esplicitamente il parametro del template, generando una funzione che si attende due valori double. A questo punto, il parametro "2" viene automaticamente convertito in "double", come avviene quando in linguaggio C si passa un valore intero a una funzione che si attende un parametro di tipo "double".
Le altre due versioni convertono il valore "2" nel corrispondente valore "double".