Templates C++

Video:

Contenido:
00:02 – Sobrecarga de función
02:00 – Templates de función
02:20 – Declaración y llamada a Template
04:20 – ¿Por qué usar Template?
04:50 – Deduccion de parametro de Template
05:43 – Funcionamiento de las Templates
07:23 – Valores constantes como parametro de Template
09:35 – Cuidados al usar Templates

Índice:

  1. Problema: Función ‘max’.
    1. Solución con Sobrecarga.
    2. Solución con Templates.
      1. Sintaxis – Declaración de Template de función.
      2. Llamada a Template de función.
      3. Deducción de argumento de Template
  2. Templates vs Sobrecarga.

1. Problema: Función ‘max’

Sobrecarga

Tenemos un array de 3 elementos int como el siguiente:

    int arri[3] {2, 6, 9};

Y queremos una función ‘max’ que retorne el máximo elemento de dicho array, entonces la función puede ser esta:

int max(int arr[3]) {
    /* retorna el máximo valor en
     un array que contiene N elementos */
    int max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

Ahora podemos llamar a nuestra función e imprimir la respuesta:

cout<<max(arri)<<"\n"; // OUTPUT: 9

Esto funciona correctamente pero que pasa si ahora tenemos un array de 3 elementos de tipo double, veamos:

Si hacemos intentamos llamar a la función max para un array de double:

double arrd[2] {2.3, 4.5};
cout<<max(arrd)<<"\n";

Obtendremos un error como este:

main.cpp: In function 'int main()':
main.cpp:19:19: error: no matching function for call to 'max(double [3])'
     cout<<max(arrd)<<"\n";
                   ^

El compilador no puede encontrar la función max(double [3]), para solucionar esto podemos crear dicha función, (es decir la misma función ‘max’ pero ahora para double)

double max(double arr[3]) {
    /* retorna el máximo valor en
     un array que contiene N elementos */
    double max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

Nuestro código va quedando así:

#include <iostream>
using namespace std;

int max(int arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    int max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

double max(double arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    double max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

int main() {

    int arri[3] {2, 6, 9};
    cout<<max(arri)<<"\n";

    double arrd[3] {2.3, 4.5, 1.5};
    cout<<max(arrd)<<"\n";

    return 0;
}

Si ejecutamos, la salida es:

9
4.5

Nuestro programa funciona, pero si ahora queremos hacer lo mismo ahora con un array de char, el código quedaria de la siguiente forma:

#include <iostream>
using namespace std;

int max(int arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    int max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

double max(double arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    double max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

char max(char arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    char max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

int main() {

    int arri[3] {2, 6, 9};
    cout<<max(arri)<<"\n";

    double arrd[3] {2.3, 4.5, 1.5};
    cout<<max(arrd)<<"\n";

    char arrc[3] {'a', 'z', 'k'};
    cout<<max(arrc)<<"\n";

    return 0;
}

Esta forma resolver nuestro problema inicial de la función ‘max’ se conoce como Sobrecarga.

Ahora veamos otra forma de resolver este problema, la cual es el titulo del video, con Templates:

Templates

Sintaxis:

función ‘max’ con Templates:

template<typename T>
T max(T arr[3]) {
    /* retorna el máximo valor en
     un array que contiene 3 elementos */
    T max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

template y typename son palabras clave en C++, la palabra template es obvio su uso y typename (que se puede reemplazar por class) indica que lo de su derecha ( ‘T’ ) es un tipo de dato. ‘T’ solo es un nombre puede ser cualquier otra nombre como ‘MiTipo’, ‘TipoDato’, etc.

Las templates parametrizan el tipo de dato, para que nuestra funcion, ‘max’ en este caso no dependa del tipo de dato que utilizamos, sino que centre más en el algoritmo que en el tipo de dato.

Llamada a template

La llamada a templates de función se da así:

max<int>(arri); // T = int 
max<double>(arrd); // T = double
max<char>(arrc); // T = char

La llamada a template de función recibe dos tipos de parámetro, uno es el parámetro de plantilla va entre los signos ‘<‘ y ‘>’ (este parámetro se pondrá en T) los cuales en las llamadas anteriores son ‘int’, ‘double’, y ‘char’. El segundo tipo de parámetro son los parámetros de la función misma, la que va entre paréntesis en nuestra función habitual.

Nuestro código con Templates:

template<typename T>
T max(T arr[3]) {
    /* retorna el máximo valor en
     un array que contiene N elementos */
    T max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

int main() {

    int arri[3] {2, 6, 9};
    cout<<max<int>(arri)<<"\n";

    double arrd[3] {2.3, 4.5, 1.5};
    cout<<max<double>(arrd)<<"\n";

    char arrc[3] {'a', 'z', 'k'};
    cout<<max<char>(arrc)<<"\n";

    return 0;
}

Si comparamos la solución con Sobrecarga y con Templates, el codigo con Templates es mucho mejor, ya que si usamos otro array de otro tipo como float por ejemplo, el codigo con sobrecarga crece más y más, mientras que el código con Templates queda igual.

Deducción de tipo

Esta característica ayuda a que el código con templates se torne menos verboso. Veamos lo que es:

Se le dice deducción de tipo cuando llamamos a una Template sin especificar los parámetros de plantilla y el compilador deduce el argumento de nuestra template según la información que tenga en la declaración de función o en su llamada.

Si nosotros llamamos:

max(3.4, 5.42313); 

Aquí no especificamos el argumento de plantilla, simplemente llamamos a la función como cualquier otra, y esto es posible gracias a que el compilador deduce el parámetro de plantilla el cual es <int> en este caso. Así que podríamos modificar nuestro código y dejarlo finalmente de la siguiente manera:

template<typename T>
T max(T arr[3]) {
    /* retorna el máximo valor en
     un array que contiene N elementos */
    T max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

int main() {

    int arri[3] {2, 6, 9};
    cout<<max(arri)<<"\n"; // deduccion de tipo: 'int'

    double arrd[3] {2.3, 4.5, 1.5};
    cout<<max(arrd)<<"\n"; // deduccion de tipo: 'double'

    char arrc[3] {'a', 'z', 'k'};
    cout<<max(arrc)<<"\n"; // deduccion de tipo: 'char'

    return 0;
}

Templates vs Sobrecarga

¿Eficiencia?

Usar Templates en lugar de Sobrecarga hace que nuestro código sea más legible y mantenible, ya que si por ejemplo si queremos modificar nuestra funcion en el caso de la sobrecarga necesitamos cambiar cada función. sin embargo con Templates nuestra función solo aparece una vez por lo que aparte de que es más facil de leer es facil de modificar.

Sin embargo cuando hablamos de eficiencia, no significa que usar templates hace nuestro código más pesado que usando sobrecarga, ya que lo que pasa es que cuando usamos templates el compilador genera el código que necesita, es decir las templates de función no son nada por si mismas, cuando nosotros llamamos:

max<int>(arri);

El compilador genera la función que necesita es decir genera:

int max(int arr[3]) {
    /* retorna el máximo valor en
     un array que contiene N elementos */
    int max = 0;
    for (int i=0; i<3; i++)
        if (arr[i]>max) max = arr[i];
    return max; 
}

Y lo mismo con las demás llamadas de función, es decir nuestro código al final de la compilación queda de la misma manera con templates que con sobrecarga. La diferencia es que con templates nosotros no tenemos que escribir el código sino que el compilador toma la template (o plantilla) y genera la función que necesite, esto se conoce como ‘metaprogramación’ y nos ayuda a que desde nuestro lado el código simplemente no se vea tan grande como con Sobrecarga.

Hay muchísimas más características de las templates, como por ejemplo que en los parámetros de templates no solo puedes pasar tipos de datos sino también valores constantes que se verá en otro post, y muchas más características. Y las templates tienen muchos más usos, podemos usar templates no solo para funciones sino también para clases o estructuras.


Advertencia: Este post puede contener errores, si encuentra uno puede colocarlo en los comentarios para contribuir a mejorar esta web.
Puede escribir al autor en rogrp6@gmail.com

¡Gracias por visitar este post!

Un comentario sobre “Templates C++

Deja un comentario