Curso de C++ v2.0
Consultas, lista de correo 'C++ Con Clase' 'C++ Con Clase' página de entrada Librerías estándar C Tabla de contenido Contactar con Webmaster
*Introducción
*1 Toma de contacto
*2 Variables I
*3 Funciones I: Declaración y definición
*4 Operadores I
*5 Sentencias
*6 Declaración de variables
*7 Normas para la notación
*8 Cadenas de caracteres
*9 Conversión de tipos
*10 Variables II: Arrays
*11 Variables III: Estructuras
*12 Variables IV: Punteros 1
*13 Operadores II: Más operadores
*14 Operadores III: Precedencia
*15 Funciones II: Parámetros por valor y referencia
*16 Variables V: Uniones
*17 Variables VI: Punteros 2
*18 Operadores IV: De bits y condicional
*19 Definición de tipos
*20 Funciones III
*21 Funciones IV: Sobrecarga
*22 Operadores V: Sobrecarga
*23 El preprocesador
*24 Funciones V: Recursividad
*25 Variables VII: Modificadores
*26 Espacios con nombre
*27 Clases I: Definiciones
*28 Declaración de clases
*29 Constructores
*30 Destructores
*31 El puntero this
*32 Sistema de protección
*33 Modificadores para miembros
*34 Más sobre funciones
*35 Operadores sobrecargados
*36 Herencia
*37 Funciones virtuales
*38 Derivación múltiple
*39 Trabajar con ficheros
*40 Plantillas
 . Sintaxis
 . Plantillas de funciones
 . Plantilla para Tabla
 . Ejemplo: plantilla Tabla
 . Tablas de cadenas
 . Plantillas como parámetros
 . Amigos de plantillas
 . Miembros estáticos
 . (Valores por defecto)
 . Ejemplo plantilla de pila
 . Librerías de plantillas
 . typename
*41 Punteros a miembros
*42 Castings
*43 Excepciones
*Ejemplos capítulos 1 a 6
*Ejemplos capítulos 8 y 9
*A Palabras reservadas C/C++
*B Trigrafos y símbolos alternativos
*C Librerías estándar
*D Streams
<< < > >>

Funciones que usan plantillas como parámetros.  

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.

Pasar una instancia de una plantilla.

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.

Pasar una plantilla genérica.

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.

Amigos de plantillas.  

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:

Clase o función amiga de una plantilla:

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.

Clase o función amiga de una instancia de una plantilla:

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.

<< < > >>