2012-07-13 11 views
16
#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; 
} 

Sírvanse explicar por qué no es válido std::result_of<f(int)>::type x; ...std :: result_of función simple

cppreference dice "(std::result_of) deduce el tipo de retorno de una expresión llamada de función al tipo de compilación.".

¿cuál es el problema?

+3

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

Respuesta

22

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:

  1. F es un puntero a una función miembro de una clase T
  2. Si sólo hay un argumento, F es un puntero a un miembro de datos de la clase T, o,
  3. 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.)

Cuestiones relacionadas