std :: result_of和decltype之间的区别


100

我在理解std::result_ofC ++ 0x 的需求时遇到了一些麻烦。如果我理解正确,result_of它用于获得使用某些类型的参数调用函数对象的结果类型。例如:

template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
    return f(a);
}

我看不出以下代码有什么区别:

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
    return f(a);
}

要么

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
    return f(a);
}

我可以用这两种解决方案看到的唯一问题是我们需要:

  • 有一个仿函数的实例,以在传递给decltype的表达式中使用它。
  • 知道函子的已定义构造函数。

我是否正确地认为decltype和之间的唯一区别result_of是第一个需要表达而第二个不需要?

Answers:


86

result_of在Boost引入,然后在TR1引入,最后在C ++ 0x 中引入。因此result_of具有向后兼容(具有合适的库)的优点。

decltype 是C ++ 0x中的一个全新事物,不仅限于函数的返回类型,而且是一种语言功能。


无论如何,在gcc 4.5上,result_of实现方式如下decltype

  template<typename _Signature>
    class result_of;

  template<typename _Functor, typename... _ArgTypes>
    struct result_of<_Functor(_ArgTypes...)>
    {
      typedef
        decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
        type;
    };

4
据我了解,decltype它不仅丑陋而且功能强大。result_of只能用于可调用的类型,并且需要类型作为参数。例如,您不能result_of在这里使用:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );如果参数可以是算术类型(没有F可以定义的函数F(T,U)表示t+u。对于用户定义的类型,可以这样)(同样,我还没有真正使用过)这对成员方法的调用可能很难有做result_of不使用粘合剂或lambda表达式
dribeas大卫-罗德里格斯

2
需要注意的是,decltype需要参数来使用AFAIK调用函数,因此如果没有result_of <>,则不依赖于具有有效默认构造函数的参数来获取模板返回的类型是很尴尬的。
罗伯特·梅森

3
@RobertMason:可以使用检索这些参数std::declval,就像上面显示的代码一样。当然,这很丑陋:)
kennytm 2012年

1
@DavidRodríguez-dribeas您的评论中有一个未封闭的括号,以“(有)::
Navin

1
另一个注意事项:自C ++ 17起,不推荐使用result_of其辅助类型,result_of_t而推荐使用invoke_resultand invoke_result_t,从而减轻了前者的一些限制。这些列在en.cppreference.com/w/cpp/types/result_of的底部。
sigma

11

如果您需要的不是函数调用之类的类型,std::result_of则不适用。decltype()可以给您任何表达式的类型。

如果我们仅使用确定函数调用返回类型的不同方式(在std::result_of_t<F(Args...)>和之间decltype(std::declval<F>()(std::declval<Args>()...)),则存在差异。

std::result_of<F(Args...) 定义为:

如果将表达式 INVOKE (declval<Fn>(), declval<ArgTypes>()...)当成未求值的操作数时格式正确(第5条),则成员typedef类型应命名该类型,decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); 否则,将不存在成员类型。

result_of<F(Args..)>::type和之间的区别仅在decltype(std::declval<F>()(std::declval<Args>()...)于此INVOKE。除了直接键入declval/ decltype之外,直接使用/ 只是在F可以直接调用(函数对象类型或函数或函数指针)的情况下才有效。result_of另外支持指向成员函数的指针和指向成员数据的指针。

最初,使用declval/ decltype保证使用SFINAE友好的表达式,而std::result_of可能会给您带来硬错误而不是推论失败。这已在C ++ 14中得到纠正:std::result_of现在需要对SFINAE友好(感谢本文)。

因此,在符合标准的C ++ 14编译器上,std::result_of_t<F(Args...)>绝对是优越的。它更清晰,更短且正确支持更多Fs


除非是这样,否则您是在不想允许指向成员的指针的上下文中使用它,因此std::result_of_t在您可能希望它失败的情况下会成功。

例外。尽管它支持指向成员的指针,但是result_of如果您尝试实例化无效的type-id,则将不起作用。这些将包括返回函数或按值获取抽象类型的函数。例如:

template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }

int answer() { return 42; }

call(answer); // nope

正确的用法应该是result_of_t<F&()>,但是您不必记住它的细节decltype


对于非引用类型T和函数template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); },用法result_of_t是否正确?
紫政大同

另外,如果我们传递给的参数fconst T,我们应该使用result_of_t<F&&(const T&)>吗?
zizheng Tai
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.