std::result_of<T>
requiere T
para ser un tipo --pero no cualquier tipo. T
debe haber un tipo de función por lo que se utiliza esta especialización parcial de result_of
:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
tal que:
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
está bien formado (C++ 11 20.9.7.6) . (INVOKE se define en 20.8.2.)
La razón por la que std::result_of<f(int)>
no funciona es porque f
no es un tipo --es una instancia de un tipo de función. Declarar x
a ser el tipo de retorno de f
aplicado a un int
, sólo tiene que escribir esto:
decltype(f(int{})) x;
o si lo prefiere la codificación dura un int
:
decltype(f(32)) x;
Si el tipo de de f
se desea luego utilizar:
using FuncPtr = decltype(f);
En el código proporcionado F
(es decir, no minúsculas f
), sin embargo es un tipo y así F(int)
define un tipo que representa la función que devuelve F
aceptar una int
como un argumento. ¡Claramente esto no es lo que es F! El tipo F
es una estructura cuyas instancias pueden usar el operador de llamada a función. F
tampoco tiene constructores explícitos o implícitos que tomen un int
, etc. ¿Cómo puede esto funcionar? Respuesta corta: Plantilla "mágica".
Básicamente, la definición de std::result_of
toma el tipo, F(int)
y separa el tipo de devolución de los tipos de argumento para que pueda determinar qué caso de INVOKE() le permitiría funcionar. Los casos de Invoke son:
- F es un puntero a una función miembro de una clase T
- Si sólo hay un argumento, F es un puntero a un miembro de datos de la clase T, o,
- una instancia de F se puede utilizar como una función, es decir,
declval<F>()(declval<int>())
que puede ser una llamada a la función normal o algún tipo de funtor (por ejemplo, como su ejemplo).
Una vez que esto se haya determinado, result_of
puede determinar el tipo de devolución de la expresión válida. Esto es lo que se devuelve a través del miembro result_of
de type
.
Lo bueno de esto es que el usuario de result_of
no necesita saber nada sobre cómo funciona esto realmente. Lo único que uno debe entender es que result_of
necesita una función TYPE. Si uno está utilizando nombres que no son tipos dentro del código (por ejemplo, f
), necesitará decltype
para obtener el tipo de expresión con tal.
Finalmente, parte de la razón por la cual f
no se puede considerar como un tipo es porque los parámetros de la plantilla también permiten valores constantes y f
es un valor de puntero de función constante. Esto se demuestra fácilmente (usando la definición de la cuestión de f
):
template <double Op(int)>
double invoke_op(int i)
{
return Op(i);
}
y posterior:
std::cout << invoke_op<f>(10) << std::endl;
Así que para obtener el tipo de valor de retorno de una expresión invocar adecuadamente f
con un poco de int
uno escribiría:
decltype(f(int{}))
(Nota: f
nunca es llamado: el compilador simplemente usa la expresión dentro de decltype
para determinar su resultado es decir, su valor de retorno en este caso.)
La solución es utilizar esta 'std :: result_of :: escriba x; '. Si quieres una comprensión más profunda, entonces mira esta pregunta en http://stackoverflow.com/q/2763824/893693 –
inf