Function Templates y Deduccion de Tipo
Aprende a escribir funciones que funcionan con cualquier tipo. Comprende la deduccion de argumentos de template, los parametros non-type, y por que los templates viven en los headers.
Por Que Function Templates?
Sin templates, escribir una funcion que funcione para multiples tipos significa duplicar la logica. Escribirias int max(int, int), double max(double, double), std::string max(std::string, std::string) — logica identica repetida para cada tipo. Las function templates te permiten escribir el algoritmo una vez y dejar que el compilador genere versiones especificas para cada tipo automaticamente.
Una function template no es una funcion — es un plano (blueprint) a partir del cual el compilador instancia funciones reales cuando las usas con tipos especificos. Esto ocurre completamente en tiempo de compilacion, produciendo codigo identico al que escribirias a mano — costo cero.
Sintaxis de Templates
Una function template se introduce con la palabra clave template seguida de una lista de parametros entre angulos. Puedes usar typename o class indistintamente para parametros de tipo — significan exactamente lo mismo. La convencion moderna prefiere typename.
#include <iostream>
#include <string>
// 'typename' and 'class' are interchangeable here
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Equivalent — 'class' means the same thing
template <class T>
T minimum(T a, T b) {
return (a < b) ? a : b;
}
int main() {
std::cout << maximum(3, 7) << '\n'; // deduces T = int
std::cout << maximum(3.14, 2.71) << '\n'; // deduces T = double
std::cout << maximum<std::string>("hello", "world") << '\n';
// explicit instantiation needed — "hello" is const char*, not std::string
}Reglas de Deduccion de Argumentos de Template
El compilador deduce los argumentos de template comparando los tipos de los argumentos de la funcion contra los patrones de parametros. Las reglas clave son:
1. Se prefiere la coincidencia exacta — maximum(3, 7) deduce T = int porque ambos argumentos son int.
2. No hay conversiones implicitas durante la deduccion — maximum(3, 7.0) es un error: el compilador ve T = int del primer argumento y T = double del segundo, y se niega a elegir uno.
3. Decaimiento de array a puntero — cuando T aparece como parametro por valor, los arrays decaen a punteros. Usa T(&)[N] para capturar el tipo del array y su tamano.
4. El const/volatile de nivel superior se elimina de los parametros por valor pero se preserva para parametros de referencia/puntero.
5. Colapso de referencias — si T se deduce como un tipo referencia (en forwarding references), se aplican las reglas de colapso de referencias: T& & se convierte en T&, T& && se convierte en T&, T&& & se convierte en T&, T&& && se convierte en T&&.
Cuando la deduccion falla o es ambigua, debes proporcionar los argumentos de template explicitamente: maximum.
Parametros de Template Non-Type
Los parametros de template no se limitan a tipos. Los parametros de template non-type (NTTPs) aceptan valores en tiempo de compilacion como enteros, punteros, o (desde C++20) tipos de punto flotante y tipos de clase. Son poderosos para incorporar constantes en tiempo de compilacion al sistema de tipos.
#include <iostream>
#include <array>
#include <cstddef>
// Non-type parameter: N is a compile-time size_t value
template <typename T, std::size_t N>
T sum(const std::array<T, N>& arr) {
T total{};
for (const auto& elem : arr) {
total += elem;
}
return total;
}
// C++20: auto non-type parameter accepts any structural type
template <auto Value>
constexpr auto doubled = Value * 2;
int main() {
std::array<int, 4> nums = {1, 2, 3, 4};
std::cout << sum(nums) << '\n'; // 10, deduces T=int, N=4
std::cout << doubled<21> << '\n'; // 42
std::cout << doubled<3.14> << '\n'; // 6.28 (C++20)
}Cada combinacion unica de argumentos de template hace que el compilador genere una instanciacion separada — una copia distinta de la funcion en tu binario. Llamar a maximum, maximum y maximum produce tres funciones. Esto generalmente esta bien, pero en bases de codigo grandes los templates pueden contribuir al code bloat (binarios mas grandes, tiempos de compilacion mas largos). Las estrategias de mitigacion incluyen: usar declaraciones extern template para controlar donde ocurren las instanciaciones, extraer el codigo independiente de tipo fuera de los templates, y usar type erasure cuando el polimorfismo en tiempo de ejecucion es aceptable.
Dado que el compilador necesita ver la definicion completa del template para instanciarlo, los templates no pueden dividirse en archivos .h / .cpp como las funciones regulares. Si pones la definicion de un template en un archivo .cpp e intentas usarlo desde otra unidad de traduccion, obtendras un error del enlazador (referencia indefinida). La solucion estandar es poner toda la definicion del template en el archivo de header. Si los tiempos de compilacion se convierten en un problema, puedes usar instanciacion explicita: pon la definicion en un archivo .cpp y agrega template int maximum para instanciar explicitamente las especializaciones que necesitas.
Instanciacion Explicita
Puedes controlar explicitamente donde y cuando se instancian los templates. Esto puede reducir los tiempos de compilacion en proyectos grandes al prevenir instanciaciones redundantes entre unidades de traduccion.
// ---- maximum.h ----
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Suppress implicit instantiation in every TU that includes this header
extern template int maximum<int>(int, int);
extern template double maximum<double>(double, double);
// ---- maximum.cpp ----
#include "maximum.h"
// Explicit instantiation: generate code here, once
template int maximum<int>(int, int);
template double maximum<double>(double, double);- Las function templates son planos (blueprints) — el compilador genera funciones concretas para cada conjunto de argumentos de template
typenameyclassson intercambiables en las listas de parametros de template- La deduccion de argumentos de template no realiza conversiones implicitas — las deducciones ambiguas son errores
- Los parametros de template non-type incorporan valores en tiempo de compilacion al sistema de tipos
- Los templates deben definirse en headers (o usar instanciacion explicita) porque el compilador necesita la definicion completa para instanciar
Quiz — Pon a prueba tus conocimientos
(15 XP)1. Que sucede cuando llamas a `maximum(3, 7.0)` con un template `template<typename T> T maximum(T a, T b)`?
2. Por que las definiciones de templates tipicamente deben colocarse en archivos de header?
3. Cual es el proposito de `extern template int maximum<int>(int, int);`?