8

这个答案有一个这样的代码片段:

template<class T, class F>
auto f(std::vector<T> v, F fun)
    -> decltype( bool( fun(v[0] ) ), void() )
{
  // ...
}

它真的可以编译和工作(至少在 Ideone 上)。

那么,这种情况下的类型是如何推导出来的呢?

c++11标准真的允许下一行吗?

decltype( bool( fun(v[0] ) ), void() )

我快速浏览了一下,它看起来并不有效。在这种情况下ideone错了吗?


c++11 标准中的所有示例都使得它们在 decltype 中都只有一种类型:

struct A {
  char g();
  template<class T> auto f(T t) -> decltype(t + g())
  { return t + g(); }
};

另一个例子 :

void f3() {
  float x, &r = x;
  [=] {
  decltype(x) y1;
  decltype((x)) y2 = y1;
  decltype(r) r1 = y1;
  decltype((r)) r2 = y2;
};

和另一个

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = i;
decltype(i) x2;
decltype(a->x) x3;
decltype((a->x)) x4 = x3;

他们都在 decltype 中只有一个参数。顶部代码怎么会有两个参数(用逗号分隔)?


我创建了另一个示例(无法编译):

#include <vector>
#include <iostream>

template<class T, class F>
auto f(std::vector<T> v, F fun) -> decltype(bool(fun(v[0])), void())
{
  // ...
  (void)v;(void)fun;

  return fun(v.size());
}

void ops(int)
{
}

int main(){
  std::vector<int> v;
  f(v, [](int){ return true; });
  f(v,ops);
}

即使删除了该行,模板函数f(v,ops);的返回类型也会被评估为 void。f

4

2 回答 2

14

decltype( bool( fun(v[0] ) ), void() )使用逗号运算符

打破它,

bool( fun(v[0] ) ), void()

由两个表达式组成;首先

bool( fun(v[0] ) )

被评估为1并被丢弃,为整个表达式提供值

void()

这是type的值2void

decltype然后产生表达式的类型,如上所示void

这里使用逗号操作符的原因是为了保证整个表达式只有在第一个子表达式有效时才有效;这是因为如果第一个子表达式无效,它在 SFINAE 中用于将其排除在替换考虑之外。

之所以可行,是因为尽管decltype在语法上看起来像一个函数,但它实际上是一种语言结构,(如sizeof)被定义为采用单个参数。用括号括起来可能更清楚 comma-operator 参数:

decltype( ( bool( fun(v[0] ) ), void() ) )

笔记

  1. 该表达式实际上bool( fun(v[0] ) )并未被评估,因为我们处于未评估的上下文中(,类似于)。这里重要的是,如果整个表达式被评估,它将被评估,因此如果子表达式无效,则整个表达式无效。decltypesizeof
  2. void()不是真正的值,但它在逗号运算符 and 的上下文中表现得像一个值decltype
于 2012-08-02T10:38:10.380 回答
7

decltype产生括号之间表达式的类型,而不实际评估它(在接下来的部分中记住这一点)。

运算符计算,左参数/表达式,丢弃结果,计算右参数,并产生该结果。因此,返回类型变为void.

bool(fun(v[0]))部分而言,这相当容易。bool(f(...))根据调用的结果构造一个临时的 bool f。如果的返回类型f不能转换为bool,这将触发一个错误,这将导致 SFINAE 由于在里面decltype(这称为“表达式 SFINAE”)。

f(v[0])v[0]将传递to的返回值f,它的类型是T&。如果f没有T&可转换的参数,或者需要更多/更少的参数,这将触发错误,并再次导致 SFINAE,原因与上述相同。

std::vector(如果不支持也会发生同样的情况operator[]。)

于 2012-08-02T10:42:20.497 回答