Creazione ed Uso di Funzioni Template in C++

Mettiamo che dobbiamo creare una funzione che calcoli la media di due numeri, il C++ è un linguaggio fortemente tipizzato, quindi possiamo creare una funzione per un solo tipo di dato. Possiamo pensare allora di utilizzare più funzioni in overloading, ma questo implicherebbe la scrittura di una funzione per ogni tipo di dato. Possiamo allora far uso di macro, come questa:

#define MEDIA(A, B) ((A + B) / 2)

ma l’uso di questa tecnica è potenzialmente pericolosa per errori a tempo di esecuzione. Per affrontare questo tipo di problematiche, il C++ mette a disposizione un meccanismo: le funzioni template o funzioni modello. La parola di modello di funzione fa intendere un’entità astratta che prima di essere utilizzata deve essere istanziata e trasformata in una funzione reale. Vediamo come risolvere la funzione della media con l’uso dei template:

template <class X>
X media(X a, X b)
{
   return (a + b) / 2;
}

Con questo tipo di funzione possiamo scrivere:

int iVal1 = 8;
int iVal2 = 12;

double dVal1 = 12.54;
double dVal2 = 76.43;

printf("La media di %d e %d: %d\n",
    iVal1, iVal2, media(iVal1, iVal2));
printf("La media di %.2f e %.2f: %.2f\n",
   dVal1, dVal2, media(dVal1, dVal2));

In fase di compilazione del programma, il compilatore non opera un controllo sui tipi, ma controlla la definizione del modello della funzione, istanzia il tipo X con il tipo dei parametri coinvolti. In questo caso, nella prima chiamata il compilatore prova ad applicare il modello che è stato definito con il tipo int e lo applica tutte le volte che viene utilizzato questo tipo di dati, mentre crea un’altra funzione se il tipo cambia, come nella nostra seconda chiamata. Il template dunque definisce una funzione per ogni tipo utilizzato, ma queste chiamate devono essere compatibili con il modello, se per esempio inseriamo due argomenti di tipi differenti allora il compilatore solleverà un errore con il nostro modello, perché entrambi gli argomenti della funzione sono di tipo X.
La procedura che abbiamo spiegato, svolta dal compilatore, si chiama istanziazione della funzione modello e se più file della nostra applicazione fanno uso dello stesso modello di funzione, può accadere che il compilatore generi più istanze della funzione che operano con gli stessi tipi effettivi. I problemi di questo caso sorgono in fase di linking, quando il linker trovando definizione multiple di una funzione solleva un errore. Un modo per ovviare a questo errore è avere dei file header con solo il prototipo delle funzioni e soltanto un file che le implementi, in questo modo ci sarà soltanto una versione di ciascuna istanza del modello e per il linker non vi sarà nessuna ambiguità.
La parola chiave template quindi, consente di definire quelli che vanno sotto il nome di parametri formali del modello, che è un identificatore preceduto dalla parola chiave class. In un modello possiamo definire più parametri formali, unico vincolo imposto è che ogni tipo deve almeno essere inserito tra gli argomenti della funzione, ad esempio in questo esempio:

template <class X, class Y>
Y media(X, X);

il compilatore solleva un errore perché il tipo Y è utilizzato solo come tipo di ritorno e non negli argomenti. Vediamo un esempio completo nel caso volessimo fare la media dei valori contenuti in un array:

#include <stdio.h>

template <class X>
X media(X a[], int iCount)
{
   X xTot = 0;
   for (int i = 0; i < iCount; ++i)
      xTot += a[i];

   return xTot / iCount;
}

int main(int argc, char* argv[])
{
   double dVal[] = { 12.43, 23.65, 12.76, 34.87 };

   printf("La media nella serie di valori: %.2f", media(dVal, 4));
   scanf_s("k");
}

E per finire implementiamo una funzione che faccia lo swap tra due valori:

#include <stdio.h>

template <class X>
void swap(X* a, X* b)
{
   X tmp = *a;
   *a = *b;
   *b = tmp;
}

int main(int argc, char* argv[])
{
   int x = 18;
   int y = 26;

   printf("La variabile x vale %d e la variabile y vale %d\n", x, y);
   swap(&x, &y);
   printf("La variabile x vale %d e la variabile y vale %d\n", x, y);
   scanf_s("k");
}

La possibilità offerta dalle funzioni template è quella di parametrizzare programmi ed intere librerie con l’uso di intere classi modello che magari vedremo in un altro articolo.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in VC/C++. Contrassegna il permalink.