What's the difference between result_of<F(Args...> and decltype<f(args...)>?

c++, c++11

Solution

Your version doesn't work with e.g. pointers to members. A closer, but still not exact version would be:

template <class F, class... Args>
auto async(F&& f, Args&&... args)
-> future<decltype( ref(f)(forward<Args>(args)...) )>;

The only difference remaining with `std::result_of` is that this forwards the functor as an lvalue (a problem your version also shares). In other words, the result of such a call (via an `std::reference_wrapper<F>`) is `typename std::result_of<F&(Args...)>::type`.

This is an awkward situation where several components of the Standard library (to name a few, in addition to those we've just witnessed: `std::thread`, `std::bind`, `std::function`) are specified in terms of an elusive INVOKE(f, a0, a1, ..., aN) pseudo-expression, which isn't exactly equivalent to `f(a0, a1, ... aN)`. Since `std::result_of` is one of those components, and serves in fact to compute the result type of INVOKE, that's the discrepancy you're noticing.

Because there is no `std::invoke` that comes in tandem with the `std::result_of` type trait I am of the opinion that the latter is only useful for describing e.g. the return types of the relevant Standard Library components, when your code calls them. If you want a concise and self-documenting way of writing e.g. a return type (a very worthy goal for readability, compared to sprinkling `decltype` everywhere), then I recommend you write your own alias:

template<typename F, typename... A>
using ResultOf = decltype( std::declval<F>()(std::declval<A>()...) );

(If you want the alias to be used as `ResultOf<F(A...)>` instead of `ResultOf<F, A...>` then you need a little bit of machinery to pattern match over the function signature.)

An added benefit of this alias is that it is SFINAE friendly, unlike `std::result_of`. Yes, that is one more of its flaws. (To be fair though this has been amended for the upcoming Standard and implementations are following suit already.)

You would not be missing anything if you were using such a trait because you can adapt pointers to members thanks to `std::mem_fn`.

Problem

I see that `std::async` is specified as follows: ``` template <class F, class... Args> // copied out of the standard future<typename result_of<F(Args...)>::type> async(F&& f, Args&&... args); ``` I had expected it to be declared like this: ``` template <class F, class... Args> auto async(F&& f, Args&&... args) -> future<decltype(forward<F>(f)(forward<Args>(args)...)>; ``` Would that be equivalent, or is there some way in which the use of `result_of` is preferable to the use of `decltype`? (I understand that `result_of` works with types, while `decltype` works with expressions.)

Original source

Related problems