2010-04-22 33 views
82

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:Diferencia entre std :: result_of y decltype

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 veo con estas dos soluciones es que Necesitamos ya sea:

  • tenemos una instancia del functor para usarlo en la expresión pasada a decltype.
  • conocen 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 lo hace?

Respuesta

78

result_of era introduced in Boost, y luego included in TR1, y finalmente en C++ 0x. Por lo tanto, result_of tiene una ventaja que es compatible con versiones anteriores (con una biblioteca adecuada).

decltype es algo totalmente nuevo en C++ 0x, no se limita 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; 
    }; 
+0

Ok, por lo que es principalmente una clase de conveniencia para evitar la expresión fea en decltype, ¿verdad? –

+2

@Luc: Sí. :) (¡Tarde!) – GManNickG

+2

@GMan: Mejor tarde que nunca;)! –

10

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

Si nos limitamos solo a las diferentes formas de determinar el tipo de devolución 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>()...) es bien formado cuando son tratados como un operando sin evaluar (cláusula 5), ​​el tipo typedef miembro nombrará el tipo decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); 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>()...) es todo acerca de eso INVOKE. El uso de 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, además, admite punteros a funciones de miembros y punteros a datos de miembros.

Inicialmente, utilizando declval/decltype garantiza una expresión SFINAE de usar, mientras que std::result_of que podría dar un error de hardware en lugar de un fallo de la deducción. Eso se ha corregido en C++ 14: std::result_of ahora se requiere que sea compatible con SFINAE (gracias a this paper).

Por lo tanto, en un compilador C++ 14 conforme, std::result_of_t<F(Args...)> es estrictamente superior. Es más claro, más corto y correctamente & dagger; admite más F s & Dagger;.


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

& Dagger; Con excepciones. Si bien admite punteros a miembros, result_of no funcionará si intenta crear una instancia de tipo-id no válida. Estos incluirían una función que devuelve una función o toma tipos abstractos por valor. Ej .:

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 hubiera sido result_of_t<F&()>, pero eso es un detalle que no tiene que recordar con decltype.

+0

Para un tipo de no-referencia 'T' y una función' plantilla resultado_de_t llamada (F && f, T && arg) {return std :: forward (f) (std: : mover (arg)); } ', ¿es correcto el uso de' result_of_t'? –

+0

Además, si el argumento que pasamos a 'f' es' const T', ¿deberíamos usar 'result_of_t '? –

Cuestiones relacionadas