hora obten_hora_actual() ; coordenada obten_posicion_actual() ; std::map<hora , coordenada> m; m[obten_hora_actual()] = obten_posicion_actual(); //...
Bien. Parece que no habrá problemas. Bueno, realmente no habrá problemas mientras no se modifique el tipo de retorno de las funciones obten_hora_actual() y obten_fecha_actual. Lo que realmente me gustaría expresar es que m es un mapa que usa como tipo para la clave el tipo de retorno de la primera función y como tipo para el valor el tipo de retorno de la segunda función.
Aquí aparece en nuestra ayuda el operador decltype. Este operador, permite obtener el tipo de una expresión y se puede usar en cualquier contexto en el que se pueda usar un tipo.
hora obten_hora_actual(); coordenada obten_posicion_actual(); std::map<decltype(obten_hora_actual()), decltype(obten_posicion_actual())> m; m[obten_hora_actual()] = obten_posicion_actual(); //...
Originalmente, algunos fabricantes habían implementado una extensión con un operador parecido: typeof. Sin embargo, la semántica de este operador era distinta, y el comité de ISO C++ decidió optar por una palabra reservada distinta. La elección de decltype puede parecerte poco afortunada, pero era la opción que menos afectaba al código ya existente.
Reglas para la evaluación de decltype
La expresión decltype(expr) se puede utilizar en cualquier tipo donde se pueda usar un especificador de tipo. De esta manera, se puede escribir el siguiente código:
int x; long y; decltype(x+y) z; // z es long
En este caso la expresión decltype(x+z) equivale al tipo long, puesto que el resultado de sumar un int y un long es un long.
En el caso de que la expresión pasada a decltype sea una variable, la regla es ligeramente distinta y el resultado es el tipo con el que se declaró la variable:
int x;
int & rx = x;
decltype(x) y = x; // int y
decltype(rx) ry = x; // int & y
Esta regla, hace que en este caso la deducción de tipos no funcione exactamente igual con auto que con decltype:
int x;
int & rx = x;
auto y1 = rx; // int y1. y1 es una copia de x
decltype(rx) y2 = rx; // int & y2 = rx. y2 es una referencia a x
Esto también es aplicable a los parámetros de una función:
template <typename T> class X { /*...*/};
void f(int x1, int & x2, const int & x3) {
X<decltype(x1)> z1; // X<int> z1;
X<decltype(x2)> z2; // X<int&> z2;
X<decltype
/*...*/
}
También se puede utilizar decltype sobre una invocación a una llamada a función. Es importante tener en cuenta que una invocación a función dentro de decltype no realiza una llamada a la función. Su único objetivo es determinar el tipo de retorno de la función.
string obten_valor(const string & clave, int indice);
void imprime() {
list<decltype(obten_valor("usuario",0))> l;
for (int i=0;i
for (int i=0;i
l.push_back(obten_valor("usuario",i));
}
}
Una diferencia bastante relevante ocurre en el caso de que decltype se aplique a una variable que se encuentre entre paréntesis. En este caso el tipo determinado por decltype es siempre una referencia.
int x;
decltype((x)) y = x; // int & y = x
De forma general, esto ocurre con cualquier expresión que no sea exactamente un nombre de variable y que pueda actuar como un l-valor.
template <class C>
void f(C & c) {
decltype(c[0]) t; // Error t es referencia sin iniciador
// ...
}
//...
vector<string> v = { "uno", "dos", "res" };
f(v);
void f(C & c) {
decltype(c[0]) t; // Error t es referencia sin iniciador
// ...
}
//...
vector<string> v = { "uno", "dos", "res" };
f(v);
En este caso, el tipo de t se obtiene evaluando la expresión decltype(v.operator[](int)) que es un l-valor (en este caso una referencia a string). Por tanto el tipo de t acaba siendo string& y la primera línea de la función f() genera un error de compilación porque se estaría declarando una variable de tipo referencia sin darle un valor inicial.
Ahora bien, la mayoría de los ejemplos empleados hasta ahora (aunque no todos) pueden parecer artificiosos y poco útiles. Probablemente, sea cierto. Sin embargo, hay contextos en los que declttype manifiesta su verdadera utilidad como la deducción automática del tipo de retorno de una función o la especificación de excepciones mediante la nueva palabra reservada noexcept.