Diferencia entre std :: result_of y decltype

Tengo algunos problemas para entender la necesidad de std::result_of en C ++ 0x. Si entendí correctamente, result_of se usa para obtener el tipo resultante de invocación de un objeto de función con ciertos tipos de parámetros. Por ejemplo:

 template  typename std::result_of::type invoke(F f, Arg a) { return f(a); } 

Realmente no veo la diferencia con el siguiente código:

 template  auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter { return f(a); } 

o

 template  auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F { return f(a); } 

El único problema que puedo ver con estas dos soluciones es que necesitamos:

  • tener una instancia del funtor para usarlo en la expresión pasada a decltype.
  • conocer un constructor definido para el functor.

¿Estoy en lo cierto al pensar que la única diferencia entre decltype y result_of es que el primero necesita una expresión mientras que el segundo no?

result_of se introdujo en Boost , y luego se incluyó en TR1 , y finalmente en C ++ 0x. Por result_of tanto, result_of tiene una ventaja que es compatible con versiones anteriores (con una biblioteca adecuada).

decltype es algo completamente nuevo en C ++ 0x, no se restringe solo al tipo de devolución de una función, y es una función de idioma.


De todos modos, en gcc 4.5, result_of se implementa en términos de decltype :

  template class result_of; template struct result_of<_Functor(_ArgTypes...)> { typedef decltype( std::declval<_functor>()(std::declval<_argtypes>()...) ) type; }; 

Si necesita el tipo de algo que no es algo así como una llamada a función, std::result_of simplemente no se aplica. decltype() puede darle el tipo de cualquier expresión.

Si nos limitamos a las diferentes formas de determinar el tipo de retorno de una llamada a función (entre std::result_of_t y decltype(std::declval()(std::declval()...) ), entonces hay una diferencia.

std::result_of se define como:

Si la expresión INVOKE (declval(), declval()...) está bien formada cuando se trata como un operando no evaluado (Cláusula 5), ​​el tipo typedef miembro nombrará el tipo decltype(INVOKE (declval(), declval()...)); de lo contrario, no habrá ningún tipo de miembro.

La diferencia entre result_of::type y decltype(std::declval()(std::declval()...) se trata de INVOKE . Utilizando declval / decltype directamente, además de ser bastante más largo de escribir, solo es válido si F es directamente invocable (un tipo de objeto de función o una función o un puntero de función). result_of también admite punteros a funciones de miembros y punteros a datos de miembro.

Inicialmente, el uso de declval / decltype garantizaba una expresión compatible con SFINAE, mientras que std::result_of podía std::result_of un error grave en lugar de una deducción. Eso se ha corregido en C ++ 14: ahora se requiere que std::result_of sea ​​compatible con SFINAE (gracias a este documento ).

Entonces en un comstackdor C ++ 14 conforme, std::result_of_t es estrictamente superior. Es más claro, más corto y correctamente admite más F s .


A menos que, es decir, lo esté usando en un contexto en el que no desee permitir punteros a los miembros, por lo que std::result_of_t tendrá éxito en un caso en el que desee que falle.

Con excepciones. Si bien admite punteros a miembros, result_of no funcionará si intentas crear una instancia de un id-tipo no válido. Estos incluirían una función que devuelve una función o toma tipos abstractos por valor. Ex.:

 template > R call(F& f) { return f(); } int answer() { return 42; } call(answer); // nope 

El uso correcto habría sido result_of_t , pero ese es un detalle que no debes recordar con decltype .