Historia de la cuestión:
Las plantillas se añadieron en C++ para una implementación eficiente de algoritmos y estructuras de datos universales. Desde el principio, se requería un mecanismo de deducción automática de tipos a partir de los argumentos de entrada, para hacer que el uso de plantillas fuera más conveniente para los desarrolladores.
Problema:
El mecanismo de deducción no siempre es evidente: surgen ambigüedades con las referencias, el enmascaramiento de tipos, las especializaciones parciales, los parámetros de plantilla por referencia y las constantes. A veces, el compilador no puede deducir el tipo o produce un resultado inesperado.
Solución:
El compilador analiza los argumentos, los compara con los parámetros de plantilla, teniendo en cuenta las reglas de los calificadores cv, referencias y punteros. Las deficiencias y limitaciones requieren una especificación explícita del tipo cuando la deducción automática no es posible.
Ejemplo de código:
template<typename T> void func(T arg) { /* ... */ } func(10); // T deducido como int func("abc"); // T deducido como const char* // Diferencia entre T arg y T& arg: template<typename T> void printRef(T& arg); // ¡No aceptará objetos temporales!
Características clave:
¿Qué pasará si una función de plantilla acepta T& y se intenta pasarle un objeto temporal?
El compilador no podrá deducir el tipo, ya que un objeto temporal no se puede pasar por una referencia no constante. Aparecerá un error de compilación.
template<typename T> void foo(T& arg); foo(42); // ¡Error!
¿Cómo funciona la deducción de tipos con arreglos?
Los arreglos "se convierten" en punteros al pasarse por valor, pero si la plantilla acepta una referencia, se conserva el tamaño del arreglo, que a menudo se utiliza para implementar plantillas de tamaño seguro.
template<typename T, size_t N> void arraySize(T (&arr)[N]) { std::cout << N; } int x[10]; arraySize(x); // Imprimirá 10
¿Por qué no siempre es correcto usar auto para almacenar los resultados de llamadas a funciones de plantilla?
Auto al deducir el tipo puede "cortar" el calificador const o ref, lo que a veces conduce a errores inesperados de copia o mutabilidad.
auto x = funcReturningRef(); // x será por valor, ¡no referencia!
Una función de plantilla de ordenación universal acepta T&, el usuario intenta pasar un objeto temporal. Como resultado, el código no se compila y el error deja perplejo al desarrollador.
Ventajas:
Desventajas:
El optimizador está implementado a través de referencias universales (T&&) usando std::forward, lo que permite trabajar correctamente con lvalue y rvalue, aumentando así el rendimiento y la flexibilidad.
Ventajas:
Desventajas: