Está en la página 1de 26

Pensamiento Computacional

III Unidad: Polimorfismo y Plantillas


Temario
• Introducción a las plantillas de función
• Definición de plantilla de función
• Funciones genéricas
• Instanciación
• Métodos genéricos
• Parámetros de-la-plantilla
• Resumen
Introducción a las plantillas de función
• El concepto de plantilla dentro de C++ nos permite disponer de un
recurso que simplifica la definición e implantación de las clases
recipiente, como listas y arreglos asociativos.
• No hay pérdida de verificación estática de tipos de datos ni de
eficiencia en la ejecución.
• De esta forma el diseño de funciones se puede hacer desde un punto
de vista genérico.
• Donde lo que se busca es aplicar el mismo tipo de algoritmo
independientemente del tipo de datos que la función va a procesar.
• Lo que nos da la potencia de no tener que definir funciones
específicas para la manipulación de tipos de datos específicos.
• Bajo esta estrategia la reutilización de código es más dinámica,
optima y eficiente.
• Sin la pérdida de ningún tipo de característica especial del lenguaje.
Definición de plantilla de función
• Las plantillas a función o funciones genéricas son un mecanismo C++
que permite definir una función mediante uno o varios parámetros.
• A partir de estas plantillas, el compilador es capaz de generar código
de funciones distintas que comparten ciertas características.
• Las funciones así generadas se denominan instancias o
especializaciones de la plantilla.
• También versiones implícitas, para distinguirlas de las versiones
codificadas manualmente (versiones explícitas).
• Este mecanismo resulta esencial para la creación de algoritmos
genéricos como los utilizados en la STL.
• Donde las funciones genéricas son utilizadas extensivamente como
miembros de clases (en especial como constructores
parametrizados).
• Para ilustrar gráficamente su utilidad utilizaremos un ejemplo clásico:
• Queremos construir una función imprimir(valor) que pueda utilizarse
para obtener la impresión del contenido de la variable valor.
• Muchas funciones harían lo mismo por diferentes tipos de datos
Ejemplo:
Funciones similares que solo se diferencian
por los tipos de datos que procesa
• El problema que presenta C++ para esta propuesta es que, al ser un
lenguaje fuertemente tipado, la declaración imprimir(valor) requiere
especificar el tipo de argumentos.
• En realidad se requiere algo así :
void max(tipoT valor);
• Y la sintaxis del lenguaje no permite que tipoT sea una variable.
• Una posible solución es sobrecargar la función imprimir(), definiendo
tantas versiones como tipos distintos debamos utilizar.
• Las funciones cumplen con las
mismas acciones.
• La necesidad de usar
diferentes tipos de datos nos
obliga a escribir varias
funciones.
• Si adicionalmente agregamos
procesamiento para una clase,
los objetos necesitan una
función.
Sobrecarga de operador
para imprimir objetos
Funciones genéricas
• La solución al problema enunciado es utilizar una función genérica
(plantilla). La sintaxis de su definición es la siguiente:

template <class T> T imprimir(T valor)


{
cout << valor << endl;
return valor;
}
• El template es un especificador de tipo, e indica que se trata de una
plantilla (es una palabra clave C++)
• <class T> es la lista de parámetros; representa el/los parámetros de-
la-plantilla (el tipo de dato).
• <typename T> hace lo mismo que la declaración anterior.
• Los parámetros de una plantilla funciona en cierta forma como los
argumentos de una macro (el trabajo de esta macro es generar código
de funciones).
• Es importante especificar que utilizamos dos conceptos distintos,
aunque relacionados: los parámetros de-la-plantilla (contenidos en la
lista template <....> ) y
• Los argumentos de-la-función (argumentos con que se invoca la
función en cada caso concreto).
• Los parámetros de la plantilla pueden ser tipos concretos, valores
constantes o plantillas (ver la gramática).
• Lo mismo que en las funciones explícitas, las genéricas pueden ser
declaradas antes de su utilización:
template <class T> T imprimir(T);
• Y definidas después:

template <class T> T imprimir(T valor)


{
cout << valor << endl;
return valor;
}
• La idea fundamental es que el compilador deduce los tipos concretos de los
parámetros de-la-plantilla de la inspección de los argumentos actuales utilizados
en la invocación.
• Por ejemplo, la plantilla anterior puede ser utilizada mediante las siguientes
sentencias:

int i, j;
UnaClase a, b;
...
int k = imprimir( j); // L1
UnaClase c = imprimir(a); // L2
Instanciación
• Los parámetros de la plantilla introducen ciertos identificadores
(nombres como el T de los ejemplos anteriores) en el ámbito de la
definición de esta.
• Que pueden referirse a tipos; constantes u otras plantillas (a su vez,
los tipos pueden ser básicos; extendidos o compuestos).
• Cuando la plantilla es instanciada, los identificadores son sustituidos
por tipos o constantes concretas y el código es compilado.
• Cuando los parámetros son a su vez plantillas los identificadores
involucrados son sustituidos en un proceso recursivo hasta que todos
han sido sustituidos por tipos o constantes concretos.
• La instanciación de la plantilla se produce cuando el compilador
necesita una versión concreta (especialidad) de la función genérica.
• Se genera el código apropiado en concordancia con el tipo de
argumentos actuales.
• El enlazador las refunde automáticamente en una sola definición
cuando aparecen más de una vez.
• Cuando el uso de plantilla genera el crecimiento del código es
conocida como "Code bloat“.
Métodos genéricos
• Las funciones genéricas pueden ser miembros (métodos) de clases:
Parámetros de-la-plantilla
• El especificador template <...> puede contener una lista con varios
tipos.
• Estos parámetros pueden ser tipos complejos o fundamentales.
• Por ejemplo un int; incluso especializaciones de clases genéricas y
constantes de tiempo de compilación.
• Por contra, las funciones genéricas no pueden ser argumentos.
template <class A, class B> void func(A, B);
template <class A, func(A, B)> void f(A, B); // Error!!
template <class A, int x> void func(A, int);
template <class A, size_t N> void func(A, N);
template<class T> class X {...}; // una clase genérica
template<class A, class T> void func(A, X<T>);
• A diferencia de las clases genéricas, los argumentos de las funciones
genéricas no pueden tener valores por defecto.
• Lo mismo que en las funciones explícitas, en las funciones genéricas
debe existir concordancia entre el número y tipo de los argumentos
formales y actuales.
Evitar indefiniciones
• Todos los argumentos formales de-la-plantilla (contenidos en la lista
template <....> ) deben estar representados en los argumentos
formales de la función.
• De no ser así, no habría posibilidad de deducir los valores de los tipos
de la lista <....> cuando se produzca una invocación específica de la
función.
Especificación explícita de parámetros
• En ocasiones el diseño de la función no permite determinar el tipo de
parámetro de-la-plantilla a partir de los argumentos actuales de la
invocación.
template <class T> T* build() { // función genérica
try { return T* = new T; }
catch (bad_alloc) {
cout << "Memoria agotada" << endl;
abort();
}
}
Resumen
• Son un mecanismo C++ que permite definir una función mediante
uno o varios parámetros.

• La solución al problema enunciado es utilizar una función genérica


(plantilla).
• El especificador template <...> puede contener una lista con varios
tipos.
• Todos los argumentos formales de-la-plantilla deben estar
representados en los argumentos formales de la función.

También podría gustarte