【问题标题】:What's the difference between result_of<F(Args...> and decltype<f(args...)>?result_of<F(Args...> 和 decltype<f(args...)> 有什么区别?
【发布时间】:2013-03-30 12:42:41
【问题描述】:

我看到std::async指定如下:

template <class F, class... Args>                   // copied out of the standard
future<typename result_of<F(Args...)>::type>
async(F&& f, Args&&... args);

我原以为它会这样声明:

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

这是否是等效的,或者是否有某种方式可以使用result_of 比使用decltype 更可取? (我知道result_of 适用于类型,而decltype 适用于表达式。)

【问题讨论】:

  • 它是指定还是实现result_of?因为这可能只是实现它的人的心血来潮,或者它可能在decltype 进入目标编译器之前就已经实现了。在 Apple 的 libc++ 中,两者都不使用。
  • @zneak:我展示的声明是从标准中复制出来的,所以std::async 是通过result_of 指定的。实现可以为所欲为,只要它们提供的行为与指定的相同。

标签: c++ c++11


【解决方案1】:

您的版本不适用于例如指向成员的指针。更接近但仍不准确的版本是:

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

std::result_of 唯一的区别在于它将函子作为左值转发(您的版本也存在这个问题)。换句话说,这种调用(通过std::reference_wrapper&lt;F&gt;)的结果是typename std::result_of&lt;F&amp;(Args...)&gt;::type

这是一个尴尬的情况,标准库的几个组件(仅举几例,除了我们刚刚目睹的:std::threadstd::bindstd::function)以难以捉摸的形式指定INVOKE(f, a0, a1, ..., aN) 伪表达式,不完全等同于f(a0, a1, ... aN)。由于std::result_of 是这些组件之一,并且实际上用于计算 INVOKE 的结果类型,这就是您注意到的差异。

因为没有 std::invokestd::result_of 类型特征同时出现,我认为后者仅用于描述例如当您的代码调用它们时,相关标准库组件的返回类型。如果您想要一种简洁且自我记录的写作方式,例如一个返回类型(一个非常有价值的可读性目标,而不是到处撒decltype),那么我建议你编写自己的别名:

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

(如果您希望将别名用作ResultOf&lt;F(A...)&gt; 而不是ResultOf&lt;F, A...&gt;,那么您需要一些机制来对函数签名进行模式匹配。)

std::result_of 不同,此别名的另一个好处是它对 SFINAE 友好。是的,这是它的另一个缺陷。 (公平地说,尽管这已针对即将发布的标准进行了修改,并且实施已经在效仿。)

如果您使用这样的特性,您将不会错过任何东西,因为您可以调整指向成员的指针,这要感谢std::mem_fn

【讨论】:

  • 我修改了原代码,使decltype版本将std::forward应用到f,从而大概消除了所有函数对象都以左值处理的问题。感谢您指出这一点。
  • 我接受了这个作为答案,因为我认为decltype 版本不支持指向成员的指针的观察很重要。
【解决方案2】:

从功能的角度来看完全没有区别。但是,decltype 版本使用 trailing-return-type,这是从编程角度来看的一个区别。

在 C++11 中,std::result_of 不是绝对必要的,可以使用 decltype 代替,就像您使用的方式一样。但是std::result_of 向后兼容第三方库(旧的),例如具有result_of 的Boost。不过,我看不出有多大优势。

就个人而言,我更喜欢使用decltype,因为它更强大并且适用于任何 实体,而result_of 仅适用于可调用实体。所以如果我使用decltype,我可以在任何地方使用它。但是使用result_of,我不得不偶尔切换到decltype(即,当实体不可可调用时)。请参阅this at github,其中我使用了decltype 作为所有函数的返回类型。

【讨论】:

  • 另一个编程差异是decltype 版本需要使用forward,而return_of 版本显然不需要。
  • @KnowItAllWannabe:您已经注意到自己(在问题中),std::result_oftypes 一起使用,因此没有什么可转发的。
  • 我想你的意思是std::result_of 你写了std::result_type
  • @KnowItAllWannabe:是的。纠正了很久。刷新您的页面。并查看我编辑的答案。
  • 我指的是第二段中的std::result_type。但现在我想也许你想要它,因为你在谈论向后兼容性。
【解决方案3】:

您的问题已经有所不同了:“我知道result_of 适用于类型,而decltype 适用于表达式。”

它们都提供相同的功能(std::result_of 甚至现在以 decltype 的形式实现)并且 您的示例 的差异几乎不存在,因为您已经有了必要的变量构建一个表达式。

也就是说,当您只有类型时,差异归结为语法糖。考虑:

typename std::result_of< F( A, B, C ) >::type

对比

decltype( std::declval<F>()( std::declval<A>(), std::declval<B>(), std::declval<C>() )

请记住,std::result_of 在这些情况下是一个选项。

请注意,在某些情况下decltype 可以以std::result_of 不能使用的方式使用。考虑decltype( a + b ),据我所知,您找不到F 来创建等效的std::result_of&lt; F( A, B ) &gt;

【讨论】:

  • 你真的想在每个模板参数上使用std::declval而不是std::forward吗?
  • 适用于只有type可用的情况,请再次阅读答案。如果没有上述类型的变量,则不能使用std::forward,因为它需要一个参数。
  • 对,我忽略了你只使用类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-03
  • 2020-11-08
  • 2012-06-17
  • 2012-04-14
相关资源
最近更新 更多