DMarlon Studio

Lección 9 · 9 de 20

Plantillas de funciones · C++

Escriba una sola función de C++ para muchos tipos con template: el mayor de una lista de números, palabras o versiones, y un límite genérico.

C++ · Plantillas de funciones en DMarlon Studio: el código del ejemplo y su salida en la terminal

Una plantilla de función es un molde: template <typename T> dice que T es un tipo que se decidirá al usarla. largest busca el mayor elemento de un std::vector<T> sin saber si contiene enteros, cadenas o cualquier otra cosa.

Al llamar a largest(std::vector<int>{12, 47, 33}), el compilador deduce que T es int y genera una versión para enteros. Con el vector de std::string genera otra, que compara por orden alfabético: «zucchini» es la última. Cada tipo produce su propia función, sin coste al ejecutar.

Lo único que largest le pide a T es que se pueda comparar con <. Version define operator< (primero el número mayor y, si empata, el menor), así que la plantilla también funciona con ella y encuentra la 2.0. Si un tipo no cumple lo que pide la plantilla, el error aparece al compilar.

clampTo encaja un valor entre un mínimo y un máximo: el volumen 130 se queda en 100 y la calefacción a 3,5 grados sube a 5. Los tres parámetros son del mismo T, por eso clampTo(7, 0.5, 6.5) no compila: T no puede ser int y double a la vez. Con clampTo<double>(...) se indica el tipo explícitamente y el 7 se convierte en 7.0.

Código · main.cpp

#include <iostream>#include <string>#include <vector>// One template, many types: the compiler deduces T from the arguments.template <typename T>T largest(const std::vector<T>& items) {    T best = items[0];    for (const T& x : items)        if (best < x) best = x;       // the only thing T needs: operator<    return best;}template <typename T>T clampTo(T value, T low, T high) { return value < low ? low : (high < value ? high : value); }// Any type with operator< works, including our own.struct Version {    int major, minor;    bool operator<(const Version& o) const { return major != o.major ? major < o.major : minor < o.minor; }};int main() {    std::cout << "Highest score: " << largest(std::vector<int>{12, 47, 33}) << "\n";    std::cout << "Last word: " << largest(std::vector<std::string>{"pear", "apple", "zucchini"}) << "\n";    Version v = largest(std::vector<Version>{{1, 4}, {2, 0}, {1, 12}});    std::cout << "Latest version: " << v.major << "." << v.minor << "\n";    std::cout << "Volume: " << clampTo(130, 0, 100) << "\n";    std::cout << "Heating: " << clampTo(3.5, 5.0, 30.0) << "\n";    // clampTo(7, 0.5, 6.5) fails: T cannot be int and double. Say it explicitly:    std::cout << "Explicit T: " << clampTo<double>(7, 0.5, 6.5) << "\n";}

Salida · c++ -std=c++17 main.cpp -o main && ./main

Highest score: 47
Last word: zucchini
Latest version: 2.0
Volume: 100
Heating: 5
Explicit T: 6.5

Para recordar

  • template <typename T> delante de una función la convierte en plantilla.
  • El compilador deduce T de los argumentos y genera una versión por tipo.
  • La plantilla funciona con cualquier tipo que tenga las operaciones que usa.
  • funcion<Tipo>(...) fija el tipo a mano cuando la deducción no basta.
  • Las plantillas se escriben enteras, con su cuerpo, antes de usarlas (normalmente en un .h).

Ejercicio

Escriba una plantilla countAbove(const std::vector<T>& items, T limit) que cuente cuántos elementos son mayores que limit, y pruébela con notas (int) y con temperaturas (double).