Inferencia de tipos en plantillas

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:

  1. Si el tipo del argumento es una referencia, se ignora la parte de referencia
  2. 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:

  1. Como antes, si el tipo del argumento es una referencia, se ignora esa parte
  2. 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

Etiquetas: plantillas C++11 inferencia-de-tipos referencias-universales

Publicado el 8-19 16:08