c++ - std:: result_of simple function
c++11 result-of (1)
std::result_of<T>
requiere que T
sea un tipo --pero no cualquier tipo. T
debe ser un tipo de función para que se utilice 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 cual std::result_of<f(int)>
no funciona es porque f
no es un tipo --es una instancia de un tipo de función. Para declarar que x
es el tipo de retorno de f
aplicado a un int
, simplemente escriba esto:
decltype(f(int{})) x;
o si prefieres codificar un int
:
decltype(f(32)) x;
Si se desea el tipo de f
entonces use:
using FuncPtr = decltype(f);
En el código provisto F
(es decir, no en minúsculas f
), sin embargo, es un tipo, por lo que F(int)
define un tipo que representa la función que devuelve F
aceptando un int
como argumento. ¡Claramente esto no es lo que F es! El tipo de F
es una estructura cuyas instancias pueden usar el operador de llamada de función. F
tampoco tiene constructores explícitos o implícitos que tomen un int
, etc. también. ¿Cómo puede funcionar esto? Respuesta corta: Plantilla "magia".
Esencialmente, la definición de std::result_of
toma el tipo, F(int)
y separa el tipo de retorno de los tipos de argumentos para que pueda determinar qué caso de INVOKE () permitiría que funcionara. Los casos de INVOKE son:
- F es un puntero a una función miembro para alguna clase T
- Si solo 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 de función normal o algún tipo de functor (por ejemplo, como su ejemplo).
Una vez que se haya determinado esto, result_of
puede determinar el tipo de retorno de la expresión válida. Esto es lo que se devuelve a través del miembro de type
result_of
.
Lo bonito de esto es que el usuario de result_of
no necesita saber nada sobre cómo funciona esto realmente. Lo único que hay que entender es que result_of
necesita una función TYPE. Si uno usa nombres que no son tipos dentro del código (por ejemplo, f
), entonces se deberá usar decltype
para obtener el tipo de expresión con los decltype
.
Finalmente, parte de la razón por la que 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 f
de la pregunta):
template <double Op(int)>
double invoke_op(int i)
{
return Op(i);
}
y después:
std::cout << invoke_op<f>(10) << std::endl;
Entonces, para obtener el tipo de valor de retorno de una expresión invocando f
con algún int
se escribiría:
decltype(f(int{}))
(Nota: f
nunca se llama: el compilador simplemente usa la expresión dentro de decltype
para determinar su resultado, es decir, su valor de retorno en esta instancia).
#include <iostream>
#include <type_traits>
double f(int i)
{
return i+0.1;
}
struct F
{
public:
double operator ()(int i) { return i+0.1; }
};
int
main(int, char**)
{
std::result_of<F(int)>::type x; // ok
// std::result_of<f(int)>::type x; // error: template argument 1 is invalid
x = 0.1;
std::cerr << x << std::endl;
}
Por favor explique por qué std::result_of<f(int)>::type x;
no es válido...
cppreference dice "( std::result_of
) Deduce el tipo de retorno de una expresión de llamada de función en el tipo de compilación".
¿Cuál es el problema?