Se você precisa do tipo de algo que não é algo como uma chamada de função, std::result_ofsimplesmente não se aplica. decltype()pode fornecer o tipo de qualquer expressão.
Se nos restringirmos apenas às diferentes maneiras de determinar o tipo de retorno de uma chamada de função (entre std::result_of_t<F(Args...)>e decltype(std::declval<F>()(std::declval<Args>()...)), haverá uma diferença.
std::result_of<F(Args...) é definido como:
Se a expressão
INVOKE (declval<Fn>(), declval<ArgTypes>()...)for bem formada quando tratada como um operando não avaliado (Cláusula 5), o tipo de typedef de membro deve nomear o tipo de decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
outra forma, não deve haver nenhum tipo de membro.
A diferença entre result_of<F(Args..)>::typee decltype(std::declval<F>()(std::declval<Args>()...)tem tudo a ver com isso INVOKE. Usar declval/ decltypediretamente, além de ser um pouco mais longo para digitar, só é válido se puder Fser chamado diretamente (um tipo de objeto de função ou uma função ou um ponteiro de função). result_ofalém disso, oferece suporte a ponteiros para funções de membros e ponteiros para dados de membros.
Inicialmente, o uso de declval/ decltypegarantiu uma expressão compatível com SFINAE, mas std::result_ofpode gerar um erro grave em vez de uma falha de dedução. Isso foi corrigido no C ++ 14: std::result_ofagora é necessário ser compatível com SFINAE (graças a este artigo ).
Portanto, em um compilador C ++ 14 em conformidade, std::result_of_t<F(Args...)>é estritamente superior. É mais claro, mais curto e corretamente † suporta mais Fs ‡ .
† A menos, isto é, você está usando em um contexto em que não deseja permitir ponteiros para membros, portanto,
std::result_of_tseria bem-sucedido em um caso em que você deseja que ele falhe.
‡ Com exceções. Embora suporte ponteiros para membros, result_ofnão funcionará se você tentar instanciar um ID de tipo inválido . Isso incluiria uma função que retorna uma função ou recebe tipos abstratos 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
O uso correto teria sido result_of_t<F&()>, mas esse é um detalhe do qual você não precisa se lembrar decltype.
decltypeé mais feio, mas também mais poderoso.result_ofsó pode ser usado para tipos que podem ser chamados e requer tipos como argumentos. Por exemplo, você não pode usarresult_ofaqui:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );se os argumentos podem ser tipos aritméticos (não há nenhuma funçãoFque você possa definirF(T,U)para representart+u. Para tipos definidos pelo usuário, você poderia. Da mesma forma (eu realmente não brinquei com isso) eu imagino que chamadas para métodos de membro podem ser difíceis de fazerresult_ofsem usar ligantes ou lambdas