Tenho alguns problemas para entender a necessidade de std::result_of
em C ++ 0x. Se bem entendi, result_of
é usado para obter o tipo resultante de invocar um objeto de função com certos tipos de parâmetros. Por exemplo:
template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
return f(a);
}
Eu realmente não vejo a diferença com o seguinte código:
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
return f(a);
}
ou
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
return f(a);
}
O único problema que vejo com essas duas soluções é que precisamos:
- tem uma instância do functor para usá-lo na expressão passada para decltype.
- conheça um construtor definido para o functor.
Estou certo em pensar que a única diferença entre decltype
e result_of
é que o primeiro precisa de uma expressão enquanto o segundo não?
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 lambdasstd::declval
, como o código que mostrei acima. Claro, isso é feio :)result_of
e seu tipo auxiliarresult_of_t
são obsoletos a partir do C ++ 17 em favor deinvoke_result
einvoke_result_t
, aliviando algumas restrições do primeiro. Eles estão listados na parte inferior de en.cppreference.com/w/cpp/types/result_of .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...)>
edecltype(std::declval<F>()(std::declval<Args>()...)
), haverá uma diferença.std::result_of<F(Args...)
é definido como:A diferença entre
result_of<F(Args..)>::type
edecltype(std::declval<F>()(std::declval<Args>()...)
tem tudo a ver com issoINVOKE
. Usardeclval
/decltype
diretamente, além de ser um pouco mais longo para digitar, só é válido se puderF
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, masstd::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 maisF
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.:O uso correto teria sido
result_of_t<F&()>
, mas esse é um detalhe do qual você não precisa se lembrardecltype
.fonte
T
e uma funçãotemplate<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }
, o uso deresult_of_t
correto?f
for aconst T
, devemos usarresult_of_t<F&&(const T&)>
?