Tengo algunos problemas para comprender la necesidad de std::result_of
C ++ 0x. Si he entendido bien, result_of
se usa para obtener el tipo resultante de invocar 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 functor 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?
decltype
es más feo pero también más poderoso.result_of
solo se puede usar para tipos que son invocables y requiere tipos como argumentos. Por ejemplo, no puede usarresult_of
aquí:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
si los argumentos pueden ser tipos aritméticos (no hay una funciónF
tal que pueda definirF(T,U)
para representart+u
. Para tipos definidos por el usuario, podría hacerlo. De la misma manera (realmente no he jugado con eso) imagino que las llamadas a métodos de miembros pueden ser difíciles de realizarresult_of
sin usar carpetas o lambdasstd::declval
, como el código que he mostrado arriba. Por supuesto, esto es feo :)result_of
y su tipo de ayudaresult_of_t
están en desuso a partir de C ++ 17 a favor deinvoke_result
yinvoke_result_t
, aliviando algunas restricciones del primero. Estos se enumeran al final de en.cppreference.com/w/cpp/types/result_of .Si necesita el tipo de algo que no es algo así como una llamada de función,
std::result_of
simplemente no se aplica.decltype()
puede darte el tipo de cualquier expresión.Si nos limitamos a las diferentes formas de determinar el tipo de retorno de una llamada de función (entre
std::result_of_t<F(Args...)>
ydecltype(std::declval<F>()(std::declval<Args>()...)
), entonces hay una diferencia.std::result_of<F(Args...)
Se define como:La diferencia entre
result_of<F(Args..)>::type
ydecltype(std::declval<F>()(std::declval<Args>()...)
se trata de esoINVOKE
. Usardeclval
/decltype
directamente, además de ser un poco más largo de escribir, solo es válido siF
se puede llamar directamente (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, usar
declval
/decltype
garantizaba una expresión compatible con SFINAE, mientras questd::result_of
podría generar un error grave en lugar de un error de deducción. Eso se ha corregido en C ++ 14:std::result_of
ahora se requiere que sea compatible con SFINAE (gracias a este documento ).Entonces, en un compilador conforme a C ++ 14,
std::result_of_t<F(Args...)>
es estrictamente superior. Es más claro, más corto y correctamente † admite másF
s ‡ .† A menos que, es decir, lo esté utilizando en un contexto en el que no desea permitir punteros a los miembros, por
std::result_of_t
lo que tendría éxito en un caso en el que desee que falle.‡ Con excepciones. Si bien admite punteros a miembros,
result_of
no funcionará si intenta crear una instancia de un identificador de tipo no válido . Estos incluirían una función que devuelve una función o que toma tipos abstractos por valor. Ex.:El uso correcto habría sido
result_of_t<F&()>
, pero ese es un detalle que no tiene que recordardecltype
.fuente
T
y una funcióntemplate<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }
, ¿esresult_of_t
correcto el uso de ?f
es aconst T
, ¿deberíamos usarresult_of_t<F&&(const T&)>
?