Se você precisa do tipo de algo que não é algo como uma chamada de função, std::result_of
simplesmente 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..)>::type
e decltype(std::declval<F>()(std::declval<Args>()...)
tem tudo a ver com isso INVOKE
. Usar declval
/ decltype
diretamente, além de ser um pouco mais longo para digitar, só é válido se puder F
ser chamado diretamente (um tipo de objeto de função ou uma função ou um ponteiro de função). result_of
além disso, oferece suporte a ponteiros para funções de membros e ponteiros para dados de membros.
Inicialmente, o uso de declval
/ decltype
garantiu uma expressão compatível com SFINAE, mas std::result_of
pode gerar um erro grave em vez de uma falha de dedução. Isso foi corrigido no C ++ 14: std::result_of
agora é 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 F
s ‡ .
† A menos, isto é, você está usando em um contexto em que não deseja permitir ponteiros para membros, portanto,
std::result_of_t
seria bem-sucedido em um caso em que você deseja que ele falhe.
‡ Com exceções. Embora suporte ponteiros para membros, result_of
nã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_of
só pode ser usado para tipos que podem ser chamados e requer tipos como argumentos. Por exemplo, você não pode usarresult_of
aqui: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çãoF
que 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_of
sem usar ligantes ou lambdas