Se hai bisogno del tipo di qualcosa che non è qualcosa come una chiamata di funzione, std::result_ofsemplicemente non si applica. decltype()può darti il tipo di qualsiasi espressione.
Se ci limitiamo solo ai diversi modi per determinare il tipo di ritorno di una chiamata di funzione (tra std::result_of_t<F(Args...)>e decltype(std::declval<F>()(std::declval<Args>()...)), allora c'è una differenza.
std::result_of<F(Args...) è definito come:
Se l'espressione
INVOKE (declval<Fn>(), declval<ArgTypes>()...)è ben formata quando trattata come un operando non valutato (clausola 5), il tipo di membro typedef deve denominare il tipo decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
altrimenti, non ci sarà alcun tipo di membro.
La differenza tra result_of<F(Args..)>::typee decltype(std::declval<F>()(std::declval<Args>()...)sta tutto in questo INVOKE. L'uso diretto di declval/ decltype, oltre ad essere un po 'più lungo da digitare, è valido solo se Fè direttamente richiamabile (un tipo di oggetto funzione o una funzione o un puntatore a funzione). result_ofsupporta inoltre puntatori alle funzioni dei membri e puntatori ai dati dei membri.
Inizialmente, l'utilizzo di declval/ decltypegarantiva un'espressione amichevole per SFINAE, mentre std::result_ofpotrebbe darti un errore difficile invece di un errore di deduzione. Ciò è stato corretto in C ++ 14: std::result_ofora è necessario che sia compatibile con SFINAE (grazie a questo documento ).
Quindi su un compilatore C ++ 14 conforme, std::result_of_t<F(Args...)>è strettamente superiore. È più chiaro, più breve e correttamente † supporta più Fs ‡ .
† A meno che, cioè, non lo si utilizzi in un contesto in cui non si desidera consentire i puntatori ai membri, quindi
std::result_of_tavrebbe successo in un caso in cui si potrebbe desiderare che fallisca.
‡ Con eccezioni. Sebbene supporti i puntatori ai membri, result_ofnon funzionerà se provi a creare un'istanza di un tipo-id non valido . Questi includerebbero una funzione che restituisce una funzione o prende tipi astratti per valore. Ex.:
template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }
int answer() { return 42; }
call(answer); // nope
L'utilizzo corretto sarebbe stato result_of_t<F&()>, ma questo è un dettaglio con cui non devi ricordare decltype.
decltypeè più brutto ma anche più potente.result_ofpuò essere utilizzato solo per i tipi richiamabili e richiede i tipi come argomenti. Ad esempio, non puoi usareresult_ofqui:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );se gli argomenti possono essere tipi aritmetici (non esiste una funzioneFtale che tu possa definireF(T,U)per rappresentaret+u. Per i tipi definiti dall'utente potresti. Allo stesso modo (non ci ho davvero giocato) immagino che le chiamate ai metodi dei membri potrebbero essere difficili da fareresult_ofsenza utilizzare leganti o lambda