Es posible crear funciones que admitan parámetros que sean una plantilla. Hay dos modos de pasar las plantillas: se puede pasar una instancia determinada de la plantilla o la plantilla genérica.
Si declaramos y definimos una función que tome como parámetro una instancia concreta de una plantilla, esa función no estará disponible para el resto de las posibles instancias. Por ejemplo, si creamos una función para una tabla de enteros, esa función no podrá aplicarse a una tabla de caracteres:
// F_Tabla.cpp: ejemplo de función de plantilla para // Tabla para enteros: // C con Clase: Marzo de 2002 #include <iostream> #include "Tabla.h" suing namespace std; cont int nElementos = 5; void Incrementa(Tabla<int> &t); // (1) int main() { Tabla<int> TablaInt(nElementos); Tabla<char> TablaChar(nElementos); for(int i = 0; i < nElementos; i++) { TablaInt[i] = 0; TablaChar[i] = 0; } Incrementa(TablaInt); // Incrementa(TablaChar); // <-- Ilegal (2) for(int i = 0; i < nElementos; i++) cout << "TablaInt[" << i << "] = " << TablaInt[i] << endl; cin.get(); return 0; } void Incrementa(Tabla<int> &t) { // (3) for(int i = 0; i < t.NElementos(); i++) t[i]++; }
En (1) vemos que el argumento que especificamos es Tabla<int>, es decir, un tipo específico de plantilla: una instancia de int. Esto hace que sea imposible aplicar esta función a otros tipos de instancia, como en (2) para el caso de char, si intentamos compilar sin comentar esta línea el compilador dará error. Finalmente, en (3) vemos cómo se implementa la función, y cómo se usa el parámetro como si se tratase de una clase corriente.
Si declaramos y definimos una función que tome como parámetro una instancia cualquiera, tendremos que crear una función de plantilla. Para ello, como ya hemos visto, hay que añadir a la declaración de la función la parte "template<class T>".
Veamos un ejemplo:
// F_Tabla2.cpp: ejemplo de función de plantilla // Tabla genérica: // C con Clase: Marzo de 2002 #include <iostream> #include <cstdio> #include "Tabla.h" #include "CCadena.h" using namespace std; cont int nElementos = 5; template<class T> void Mostrar(Tabla<T> &t); // (1) int main() { Tabla<int> TablaInt(nElementos); Tabla<Cadena> TablaCadena(nElementos); char cad[20]; for(int i = 0; i < nElementos; i++) TablaInt[i] = i; for(int i = 0; i < nElementos; i++) { sprintf(cad, "Cad no.: %2d", i); TablaCadena[i] = cad; } Mostrar(TablaInt); // (2) Mostrar(TablaCadena); // (3) cin.get(); return 0; } template<class T> void Mostrar(Tabla<T> &t) { // (4) for(int i = 0; i < t.NElementos(); i++) cout << t[i] << endl; }
En (1) vemos la forma de declarar una plantilla de función que puede aplicarse a nuestra plantilla de clase Tabla. En este caso, la función sí puede aplicarse a cualquier tipo de instancia de la clase Tabla, como se ve en (2) y (3). Finalmente, en (4) vemos la definición de la plantilla de función.
Por supuesto, en el caso de las plantillas también podemos definir relaciones de amistad con otras funciones o clases. Podemos distinguir dos tipos de funciones o clases amigas de plantillas de clases:
Tomemos el caso de la plantilla de función que vimos en el apartado anterior. La función que pusimos como ejemplo no necesitaba ser amiga de la plantilla porque no era necesario que accediera a miembros privados de la plantilla. Pero podemos escribirla de modo que acceda directamente a los miembros privados, y para que ese acceso sea posible, debemos declarar la función como amiga de la plantilla.
Modificación de la plantilla Tabla con la función Mostrar como amiga de la plantilla:
// Tabla.h: definición de la plantilla tabla:
// C con Clase: Marzo de 2002
#ifndef T_TABLA
#define T_TABLA
template <class T>
class Tabla {
public:
Tabla(int nElem);
~Tabla();
T& operator[](int indice) { return pT[indice]; }
const int NElementos() {return nElementos;}
friend void Mostrar<>(Tabla<T>&); // (1)
private:
T *pT;
int nElementos;
};
// Definición:
template <class T>
Tabla<T>::Tabla(int nElem) : nElementos(nElem) {
pT = new T[nElementos];
}
template <class T>
Tabla<T>::~Tabla() {
delete[] pT;
}
#endif
Programa de ejemplo:
// F_Tabla2.cpp: ejemplo de función amiga de // plantilla Tabla genérica: // C con Clase: Marzo de 2002 #include <iostream> #include <cstdio> #include "Tabla.h" #include "CCadena.h" using namespace std; const int nElementos = 5; template<class T> void Mostrar(Tabla<T> &t); // (2) int main() { Tabla<int> TablaInt(nElementos); Tabla<Cadena> TablaCadena(nElementos); char cad[20]; for(int i = 0; i < nElementos; i++) TablaInt[i] = i; for(int i = 0; i < nElementos; i++) { sprintf(cad, "Cad no.: %2d", i); TablaCadena[i] = cad; } Mostrar(TablaInt); Mostrar(TablaCadena); cin.get(); return 0; } template<class T> void Mostrar(Tabla<T> &t) { // (3) for(int i = 0; i < t.nElementos; i++) cout << t.pT[i] << endl; }
Nota (1): aunque esto no sea del todo estándar, algunos compiladores, (sin ir más lejos los que usa Dev-C++), requieren que se incluya <> a continuación del nombre de la función que declaramos como amiga, cuando esa función sea una plantilla de función. Si te fijas en (2) y (3) verás que eso no es necesario cuando se declara el prototipo o se define la plantilla de función. En otros compiladores puede que no sea necesario incluir <>, por ejemplo, en Borland C++ no lo es.
Limitando aún más las relaciones de amistad, podemos declarar funciones amigas de una única instancia de la plantilla.
Dentro de la declaración de una plantilla podemos declarar una clase o función amiga de una determinada instancia de la plantilla, la relación de amistad no se establece para otras posibles instancias de la plantilla. Usando el mismo último ejemplo, pero sustituyendo la línea de la declaración de la clase que hace referencia a la función amiga:
template<class T> class Tabla { ... friend void Mostrar<>(Tabla<int>&); ... };
Esto hace que sólo se pueda aplicar la función Mostrar a las instancias <int> de la plantilla Tabla. Por supuesto, tanto el prototipo como la definición de la función "Mostrar" no cambian en nada, debemos seguir usando una plantilla de función idéntica que en el ejemplo anterior.
© Marzo de 2002 Salvador Pozo, salvador@conclase.net