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...)>
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 de miembro typedef nombrará el tipo; de lo decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
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 eso INVOKE
. Usar declval
/ decltype
directamente, además de ser un poco más largo de escribir, solo es válido si F
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 que std::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ás F
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.:
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 tiene que recordar decltype
.
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 lambdas