您的版本不适用于例如指向成员的指针。更接近但仍不准确的版本是:
template <class F, class... Args>
auto async(F&& f, Args&&... args)
-> future<decltype( ref(f)(forward<Args>(args)...) )>;
剩下的唯一区别std::result_of
是这会将仿函数作为左值转发(您的版本也有这个问题)。换句话说,这种调用(通过 an std::reference_wrapper<F>
)的结果是typename std::result_of<F&(Args...)>::type
。
这是一个尴尬的情况,标准库的几个组件(仅举几例,除了我们刚刚见证的组件:std::thread
, std::bind
, std::function
)是根据难以捉摸的INVOKE(f, a0, a1, ..., aN)伪表达式,它不完全等同于f(a0, a1, ... aN)
. 由于std::result_of
它是这些组件之一,并且实际上用于计算INVOKE的结果类型,这就是您注意到的差异。
因为没有std::invoke
与std::result_of
类型特征相匹配的东西,我认为后者仅用于描述相关标准库组件的返回类型,当您的代码调用它们时。如果您想要一种简洁且自我记录的编写方式,例如返回类型(与decltype
到处撒播相比,这是一个非常有价值的可读性目标),那么我建议您编写自己的别名:
template<typename F, typename... A>
using ResultOf = decltype( std::declval<F>()(std::declval<A>()...) );
(如果您希望将别名用作ResultOf<F(A...)>
而不是,ResultOf<F, A...>
那么您需要一些机制来对函数签名进行模式匹配。)
这个别名的另一个好处是它对 SFINAE 友好,不像std::result_of
. 是的,这是它的另一个缺陷。(公平地说,尽管这已针对即将发布的标准进行了修改,并且实施已经在效仿。)
如果您使用这样的特性,您将不会丢失任何东西,因为您可以调整指向成员的指针,这要归功于std::mem_fn
.