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 tipodecltype(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 ‡ .
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;
};