If you need the type of something that isn't something like a function call, std::result_of
just doesn't apply. decltype()
can give you the type of any expression.
If we restrict ourselves to just the different ways of determining the return type of a function call (between std::result_of_t<F(Args...)>
and decltype(std::declval<F>()(std::declval<Args>()...)
), then there is a difference.
std::result_of<F(Args...)
is defined as:
If the expression
INVOKE (declval<Fn>(), declval<ArgTypes>()...)
is well
formed when treated as an
unevaluated operand (Clause 5), the
member typedef type shall name the
type decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
otherwise, there shall be no member
type.
The difference between result_of<F(Args..)>::type
and decltype(std::declval<F>()(std::declval<Args>()...)
is all about that INVOKE
. Using declval
/decltype
directly, in addition to being quite a bit longer to type, is only valid if F
is directly callable (a function object type or a function or a function pointer). result_of
additionally supports pointers to members functions and pointers to member data.
Initially, using declval
/decltype
guaranteed a SFINAE-friendly expression, whereas std::result_of
could give you a hard error instead of a deduction failure. That has been corrected in C++14: std::result_of
is now required to be SFINAE-friendly (thanks to this paper).
So on a conforming C++14 compiler, std::result_of_t<F(Args...)>
is strictly superior. It's clearer, shorter, and correctly† supports more F
s‡ .
†Unless, that is, you're using it in a context where you don't want to allow pointers to members, so
std::result_of_t
would succeed in a case where you might want it to fail.
‡ With exceptions. While it supports pointers to members, result_of
will not work if you try to instantiate an invalid type-id. These would include a function returning a function or taking abstract types by value. Ex.:
template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }
int answer() { return 42; }
call(answer); // nope
The correct usage would've been result_of_t<F&()>
, but that's a detail you don't have to remember with decltype
.