c++ c++11 decltype result-of

c++ - Diferencia entre std:: result_of y decltype



c++11 result-of (2)

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<F(Args...)> y decltype(std::declval<F>()(std::declval<Args>()...) ), entonces hay una diferencia.

std::result_of<F(Args...) se define como:

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

La diferencia entre result_of<F(Args..)>::type y decltype(std::declval<F>()(std::declval<Args>()...) 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 compilador C ++ 14 conforme, std::result_of_t<F(Args...)> 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 <class F, class R = result_of_t<F()>> R call(F& f) { return f(); } int answer() { return 42; } call(answer); // nope

El uso correcto habría sido result_of_t<F&()> , pero ese es un detalle que no debes recordar con 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 F, typename Arg> typename std::result_of<F(Arg)>::type invoke(F f, Arg a) { return f(a); }

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

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

o

template <typename F, typename Arg> 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<typename _Signature> class result_of; template<typename _Functor, typename... _ArgTypes> struct result_of<_Functor(_ArgTypes...)> { typedef decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) ) type; };