Se hai bisogno del tipo di qualcosa che non è qualcosa come una chiamata di funzione, std::result_of
semplicemente 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..)>::type
e 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_of
supporta inoltre puntatori alle funzioni dei membri e puntatori ai dati dei membri.
Inizialmente, l'utilizzo di declval
/ decltype
garantiva un'espressione amichevole per SFINAE, mentre std::result_of
potrebbe darti un errore difficile invece di un errore di deduzione. Ciò è stato corretto in C ++ 14: std::result_of
ora è 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ù F
s ‡ .
† 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_t
avrebbe successo in un caso in cui si potrebbe desiderare che fallisca.
‡ Con eccezioni. Sebbene supporti i puntatori ai membri, result_of
non 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_of
può essere utilizzato solo per i tipi richiamabili e richiede i tipi come argomenti. Ad esempio, non puoi usareresult_of
qui:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
se gli argomenti possono essere tipi aritmetici (non esiste una funzioneF
tale 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_of
senza utilizzare leganti o lambda