Inferencia de tipos en plantillas
Considere el siguiente código:
template <typename T>
void f(T param) {}
int x = 5;
f(x);
En este caso, T se infiere como int debido a que x es de tipo int. Sin embargo, en la lista de parámetros de f, param puede no coincidir directamente con el tipo de T, por ejemplo:
template <typename T>
using ParamType = const T&;
template <typename T>
void f(ParamType<T> param) {}
int x = 5;
f(x);
Aquí, T se infiere como int, pero el tipo de param es ParamType, es decir, const int&
La inferencia del tipo T depende tanto del tipo del argumento pasado a la función como del tipo del parámetro
Existen tres escenarios:
- El parámetro es un puntero o una referencia, pero no una referencia universal
- El parámetro es una referencia universal
- El parámetro ni es un puntero ni una referencia
Escenario uno: Parámetro es un puntero o referencia
En este caso, se sigue esta deducción:
- Si el tipo del argumento es una referencia, se ignora la parte de referencia
- Luego, se realiza una coincidencia de patrón entre el tipo del argumento y el tipo del parámetro para determinar T
Supongamos que la definición de la plantillla de f es:
template<typename T>
void f(T& param) {
// Dentro de la función, no podemos modificar objetos const
}
Veamos tres llamadas posibles:
int x = 27; // x es de tipo int
const int cx = x; // cx es de tipo const int
const int& rx = x; // rx es de tipo const int&
f(x); // T se infiere como int, el tipo de param es int&
f(cx); // T se infiere como const int, el tipo de param es const int&
f(rx); // T se infiere como const int, el tipo de param es const int&
Como cx y rx son valores constantes, T se infiere como const int, generando así un tipo de parámetro const int&. Pasando un objeto constante a una plantilla con parámetro de tipo T&, es seguro: la constancia del objeto se mantiene como parte de TEl tipo de rx es una referencia, por lo tanto, T también se infiere como un tipo sin referencia, ya que la naturaleza de referencia se ignora durante la inferencia.
Escenario dos: ParamType es una referencia universal
Una refernecia universal es aquella que se define como T&& cuando se usa para inferencia de tipos en plantillas. Esta puede enlazarse tanto a valores izquierdos como derechos, y su tipo específico depende del argumento proporcionado:
- Si el argumento es un valor izquierdo, tanto T como param se infieren como referencias izquierdas. Primero, esta es la única situación donde T se infiere como una referencia. Segundo, aunque param se declara como una referencia derecha, el resultado final es una referencia izquiedra.
- Si el argumento es un valor derecho, se aplican las reglas normales (escenario uno) Por ejemplo:
template<typename T>
void f(T&& param); // param ahora es de tipo referencia universal
int x=27; // igual que antes
const int cx=x; // igual que antes
const int & rx=cx; // igual que antes
f(x); // x es un valor izquierdo, entonces T es int&,
// el tipo de param también es int&
f(cx); // cx es un valor izquierdo, entonces T es const int&,
// el tipo de param también es const int&
f(rx); // rx es un valor izquierdo, entonces T es const int&,
// el tipo de param también es const int&
f(27); // 27 es un valor derecho, entonces T es int,
// el tipo de param es int&&
Escenario tres: El parámetro no es ni puntero ni referencia
Esto significa que cualquier valor pasado a param será copiado Las reglas son:
- Como antes, si el tipo del argumento es una referencia, se ignora esa parte
- Si después de ignorar la referencia del argumento, este es const, se ignora también const. Lo mismo ocurre con volatile
int x=27; // igual que antes
const int cx=x; // igual que antes
const int & rx=cx; // igual que antes
f(x); // T y el tipo de param son ambos int
f(cx); // T y el tipo de param son ambos int
f(rx); // T y el tipo de param son ambos int
Argumentos de tipo arreglo
Los tipos de parámetro de arreglos se tratan como punteros, por lo tanto, pasar un arreglo a una plantilla se infiere como un tipo puntero Aunque las funciones no pueden declarar parámetros como arreglos reales, sí pueden aceptar referencias a arreglos
template<typename T>
void f(T& param);
const char name[] = "J. P. Briggs";
f(name);
T se infiere como el arreglo real, incluyendo su tamaño. En este ejemplo, T se infiere como const char[13], y el tipo del parámetro de f (la referencia al arreglo) es const char(&)[13]
Argumentos de tipo función
Los tipos de función también se desvían a punteros de función