20

我意识到“为什么事情是这样的”问题通常不是最好的,但是在 SO 上有很多人都在关注标准委员会的讨论,所以我希望可以如实回答,因为我有理由好奇什么答案是。

基本上,当我第一次看到它的模板签名时,我花了很长时间才弄清楚它发生了什么std::result_of:我认为这是我以前从未见过的模板参数的全新构造。

template< class F, class... ArgTypes >
class result_of<F(ArgTypes...)>;

经过一段时间的思考,我意识到这实际上是:F(ArgTypes...)是一个函数类型,但它不是正在评估其结果类型的函数的类型(这只是F):它是一个接受ArgTypes...参数并返回类型的函数的类型F

这不是……奇怪吗?有点骇人听闻?有谁知道委员会是否曾经讨论过任何替代方案,例如,以下...

template< class F, class... ArgTypes >
class result_of<F, ArgTypes...>;

?

我想有可能存在第二个构造不能像第一个构造那样容易使用的情况,但是哪些情况呢?

我不是想对此做出判断,但这只是我第一次看到它时让我感到困惑,所以我很好奇是否有充分的理由。我意识到部分答案可能只是“因为 Boost 做到了”,但仍然留下了剩下的(事实)问题......

  • Boost 选择这种语法来编码类型信息而不是任何替代方法是否有技术原因?

  • C++11 委员会有没有讨论过标准化这一点有多合适,因为无论如何std::result_of都可以decltype相当容易地实现它?

4

4 回答 4

18

将函数类型作为参数允许您拥有不受限制的“可变参数”类模板,即使在 C++03 中也是如此。想想看:在 C++03 中,我们没有可变参数模板。而且您不能像使用函数模板那样“重载”类模板 - 那么如何允许函数使用不同数量的“参数”呢?

使用函数类型,您可以为不同数量的参数添加任意数量的偏特化:

template<class Fty>
struct result_of;

template<class F>
struct result_of<F()>{ /*...*/ };

template<class F, class A0>
struct result_of<F(A0)>{ /*...*/ };

template<class F, class A0, class A1>
struct result_of<F(A0, A1)>{ /*...*/ };

// ...

在 C++03 中执行此操作的唯一另一种方法是默认模板参数,并针对每种情况进行部分专门化 - 缺点是它看起来不再像函数调用,并且任何result_of内部使用的包装器都不能只是传递Sig


现在,函数类型方式有一个缺点 - 您还可以对“参数”进行所有常见的转换:R(Args...)->R(*)(Args...)更重要的是T[N]->T*和顶级 cv-qualifiers 被丢弃(§8.3.5/5):

struct X{
  bool operator()(int (&&arr)[3]);
  long operator()(void*);
};

static_assert(std::is_same<std::result_of<X(int[3])>::type, bool>(), "/cry");

活生生的例子。输出:

错误:静态断言失败:/cry

另一个问题是顶级 cv 限定符被丢弃:

struct Y{};

struct X{
  bool operator()(Y const&);
  long operator()(Y&&);
};

Y const f();

static_assert(std::is_same<std::result_of<X(Y const)>::type, bool>(), "/cry");

活生生的例子。输出:

错误:静态断言失败:/cry

于 2013-03-19T01:04:37.743 回答
9

我认为只是有人认为您可以(ab)使用函数类型表示法来模仿相应仿函数调用的方式,并且它卡住了。所以,没有技术原因,只是审美原因。

// the result type of a call to (an object of) type F,
// passing (objects of) types A, B, and C as parameters.
result_of<F(A, B, C)>::type
于 2013-03-18T21:12:17.967 回答
4

result_of是 TR1 的一部分,它是在 decltype 被添加到语言之前出现的。但它是在设计时decltype考虑到的,因此更改result_ofto use的实现decltype很简单。是的,这是一个 hack,但它确实有效。

于 2013-03-18T21:09:10.800 回答
2

(这扩展了JohannesD 的答案Jesse Good 对此的评论,但这不适合评论。请投票给其他答案而不是这个答案。)

来自N1454 语法和示例

的行为的定义result_of很简单:给定类型F, T1, T2, ...,TN和左值f, t1, t2, ...,tN分别是这些类型的类型表达式

result_of<F(T1, T2, ..., TN)>::type

计算为表达式的类型f(t1, t2, ..., tN)

这不是滥用类型系统,它非常优雅!

于 2013-03-19T12:29:30.873 回答