Diferencia entre std :: result_of y decltype
Pregunta
Tengo algunos problemas para entender la necesidad de std::result_of
en C ++ 0x. Si he entendido bien, result_of
se utiliza para obtener el tipo resultante de invocar un objeto 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);
}
Yo realmente no ver 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:
- tiene una instancia de la funtor para utilizarlo en la expresión pasó a decltype.
- sabe un constructor definido para el funtor.
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?
Solución
result_of
WAS introducido en Boost , y luego incluido en TR1 , y finalmente en C 0x ++. Por lo tanto result_of
tiene una ventaja que es compatible hacia atrás (con una biblioteca adecuada).
decltype
es una cosa totalmente nueva en C ++ 0x, no restringe sólo para el tipo de retorno de una función, y es una característica del lenguaje.
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;
};
Otros consejos
Si necesita el tipo de algo que no es algo así como una llamada a la función, std::result_of
simplemente no se aplica. decltype()
le puede dar el tipo de cualquier expresión.
Si nos limitamos sólo a los diferentes maneras de determinar el tipo de retorno de una llamada de 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 así formado cuando son tratados como una operando sin evaluar (cláusula 5), ??el typedef tipo de miembro designará a las tipodecltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
de lo contrario, no habrá ningún miembro tipo.
La diferencia entre result_of<F(Args..)>::type
y decltype(std::declval<F>()(std::declval<Args>()...)
tiene que ver con que INVOKE
. Usando declval
/ decltype
directamente, además de ser un poco más largo que escribir, sólo es válida si F
es directamente exigible (un tipo de objeto de función o una función o un puntero de función). result_of
, además, apoya punteros a los miembros funciones y punteros a los datos de miembro.
En un principio, el uso de declval
/ decltype
garantiza una expresión SFINAE de usar, mientras que std::result_of
le 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 SFINAE local (gracias a este documento ).
Así que en un conformando C ++ 14 compilador, std::result_of_t<F(Args...)>
es estrictamente superior. Es más claro, más corto, y correcta † soportes más F
s ‡ .
† menos, claro está, que se está usando en un contexto en el que no desea permitir que los punteros a los miembros, por lo
std::result_of_t
tendría éxito en un caso en el que es posible que desee que falle.
‡ Con excepciones. A pesar de que es compatible con punteros a los miembros, result_of
no funcionará si intenta crear una instancia de un inválido tipo-id . Estos incluyen una función que devuelve una función o tomar 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 hubiera sido result_of_t<F&()>
, pero eso es un detalle que no tiene que recordar con decltype
.